Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b73a789dfe | |||
| 38b5ff0cec | |||
| 3c204b9b40 | |||
| 00771efeb4 | |||
| 61972d61f6 | |||
| fe40af7b7e | |||
| 12751932d1 | |||
| 41b49137a8 | |||
| c761e1de4d | |||
| a91920310d | |||
| 16e674782a | |||
| 47244daefb | |||
| 46fa104419 | |||
| 45953b3d9c | |||
| 42759e7a9f | |||
| 8e2d2c8246 | |||
| 299685775a | |||
| b7406cc4c4 | |||
| 690a0ed0d6 | |||
| a9d72a5eb1 | |||
| 6d14bb814f | |||
| be0e387ab0 | |||
| abeb67964f | |||
| bf5d10743f | |||
| 4e7aab07d5 | |||
| 15a66a2b31 | |||
| f347d44c22 | |||
| b5630f6883 |
2
.clang-format
Normal file
2
.clang-format
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ColumnLimit: 0
|
||||||
|
IndentWidth: 4
|
||||||
@ -11,21 +11,24 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
_ "unsafe"
|
_ "unsafe" // for go:linkname
|
||||||
|
|
||||||
"hakurei.app/command"
|
"hakurei.app/command"
|
||||||
"hakurei.app/container/check"
|
"hakurei.app/container/check"
|
||||||
"hakurei.app/container/fhs"
|
"hakurei.app/container/fhs"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/internal"
|
"hakurei.app/internal/dbus"
|
||||||
"hakurei.app/internal/env"
|
"hakurei.app/internal/env"
|
||||||
|
"hakurei.app/internal/info"
|
||||||
"hakurei.app/internal/outcome"
|
"hakurei.app/internal/outcome"
|
||||||
"hakurei.app/message"
|
"hakurei.app/message"
|
||||||
"hakurei.app/system/dbus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// optionalErrorUnwrap calls [errors.Unwrap] and returns the resulting value
|
||||||
|
// if it is not nil, or the original value if it is.
|
||||||
|
//
|
||||||
//go:linkname optionalErrorUnwrap hakurei.app/container.optionalErrorUnwrap
|
//go:linkname optionalErrorUnwrap hakurei.app/container.optionalErrorUnwrap
|
||||||
func optionalErrorUnwrap(_ error) error
|
func optionalErrorUnwrap(err error) error
|
||||||
|
|
||||||
func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErrs, out io.Writer) command.Command {
|
func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErrs, out io.Writer) command.Command {
|
||||||
var (
|
var (
|
||||||
@ -350,7 +353,7 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr
|
|||||||
}).Flag(&flagShort, "short", command.BoolFlag(false), "Print instance id")
|
}).Flag(&flagShort, "short", command.BoolFlag(false), "Print instance id")
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Command("version", "Display version information", func(args []string) error { fmt.Println(internal.Version()); return errSuccess })
|
c.Command("version", "Display version information", func(args []string) error { fmt.Println(info.Version()); return errSuccess })
|
||||||
c.Command("license", "Show full license text", func(args []string) error { fmt.Println(license); return errSuccess })
|
c.Command("license", "Show full license text", func(args []string) error { fmt.Println(license); return errSuccess })
|
||||||
c.Command("template", "Produce a config template", func(args []string) error { encodeJSON(log.Fatal, os.Stdout, false, hst.Template()); return errSuccess })
|
c.Command("template", "Produce a config template", func(args []string) error { encodeJSON(log.Fatal, os.Stdout, false, hst.Template()); return errSuccess })
|
||||||
c.Command("help", "Show this help message", func([]string) error { c.PrintHelp(); return errSuccess })
|
c.Command("help", "Show this help message", func([]string) error { c.PrintHelp(); return errSuccess })
|
||||||
|
|||||||
@ -1,18 +1,13 @@
|
|||||||
package main_test
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
_ "unsafe"
|
|
||||||
|
|
||||||
"hakurei.app/container/stub"
|
"hakurei.app/container/stub"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:linkname decodeJSON hakurei.app/cmd/hakurei.decodeJSON
|
|
||||||
func decodeJSON(fatal func(v ...any), op string, r io.Reader, v any)
|
|
||||||
|
|
||||||
func TestDecodeJSON(t *testing.T) {
|
func TestDecodeJSON(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@ -62,9 +57,6 @@ func TestDecodeJSON(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:linkname encodeJSON hakurei.app/cmd/hakurei.encodeJSON
|
|
||||||
func encodeJSON(fatal func(v ...any), output io.Writer, short bool, v any)
|
|
||||||
|
|
||||||
func TestEncodeJSON(t *testing.T) {
|
func TestEncodeJSON(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@ -74,7 +66,7 @@ func TestEncodeJSON(t *testing.T) {
|
|||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{"marshaler", errorJSONMarshaler{},
|
{"marshaler", errorJSONMarshaler{},
|
||||||
`cannot encode json for main_test.errorJSONMarshaler: unique error 3735928559 injected by the test suite`},
|
`cannot encode json for main.errorJSONMarshaler: unique error 3735928559 injected by the test suite`},
|
||||||
{"default", func() {},
|
{"default", func() {},
|
||||||
`cannot write json: json: unsupported type: func()`},
|
`cannot write json: json: unsupported type: func()`},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,8 +12,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/internal"
|
|
||||||
"hakurei.app/internal/env"
|
"hakurei.app/internal/env"
|
||||||
|
"hakurei.app/internal/info"
|
||||||
"hakurei.app/internal/outcome"
|
"hakurei.app/internal/outcome"
|
||||||
"hakurei.app/internal/store"
|
"hakurei.app/internal/store"
|
||||||
"hakurei.app/message"
|
"hakurei.app/message"
|
||||||
@ -24,20 +24,20 @@ func printShowSystem(output io.Writer, short, flagJSON bool) {
|
|||||||
t := newPrinter(output)
|
t := newPrinter(output)
|
||||||
defer t.MustFlush()
|
defer t.MustFlush()
|
||||||
|
|
||||||
info := &hst.Info{Version: internal.Version(), User: new(outcome.Hsu).MustID(nil)}
|
hi := &hst.Info{Version: info.Version(), User: new(outcome.Hsu).MustID(nil)}
|
||||||
env.CopyPaths().Copy(&info.Paths, info.User)
|
env.CopyPaths().Copy(&hi.Paths, hi.User)
|
||||||
|
|
||||||
if flagJSON {
|
if flagJSON {
|
||||||
encodeJSON(log.Fatal, output, short, info)
|
encodeJSON(log.Fatal, output, short, hi)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Printf("Version:\t%s\n", info.Version)
|
t.Printf("Version:\t%s\n", hi.Version)
|
||||||
t.Printf("User:\t%d\n", info.User)
|
t.Printf("User:\t%d\n", hi.User)
|
||||||
t.Printf("TempDir:\t%s\n", info.TempDir)
|
t.Printf("TempDir:\t%s\n", hi.TempDir)
|
||||||
t.Printf("SharePath:\t%s\n", info.SharePath)
|
t.Printf("SharePath:\t%s\n", hi.SharePath)
|
||||||
t.Printf("RuntimePath:\t%s\n", info.RuntimePath)
|
t.Printf("RuntimePath:\t%s\n", hi.RuntimePath)
|
||||||
t.Printf("RunDirPath:\t%s\n", info.RunDirPath)
|
t.Printf("RunDirPath:\t%s\n", hi.RunDirPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// printShowInstance writes a representation of [hst.State] or [hst.Config] to output.
|
// printShowInstance writes a representation of [hst.State] or [hst.Config] to output.
|
||||||
@ -90,12 +90,6 @@ func printShowInstance(
|
|||||||
t.Printf(" Groups:\t%s\n", strings.Join(config.Groups, ", "))
|
t.Printf(" Groups:\t%s\n", strings.Join(config.Groups, ", "))
|
||||||
}
|
}
|
||||||
if config.Container != nil {
|
if config.Container != nil {
|
||||||
if config.Container.Home != nil {
|
|
||||||
t.Printf(" Home:\t%s\n", config.Container.Home)
|
|
||||||
}
|
|
||||||
if config.Container.Hostname != "" {
|
|
||||||
t.Printf(" Hostname:\t%s\n", config.Container.Hostname)
|
|
||||||
}
|
|
||||||
flags := config.Container.Flags.String()
|
flags := config.Container.Flags.String()
|
||||||
|
|
||||||
// this is included in the upper hst.Config struct but is relevant here
|
// this is included in the upper hst.Config struct but is relevant here
|
||||||
@ -110,6 +104,12 @@ func printShowInstance(
|
|||||||
}
|
}
|
||||||
t.Printf(" Flags:\t%s\n", flags)
|
t.Printf(" Flags:\t%s\n", flags)
|
||||||
|
|
||||||
|
if config.Container.Home != nil {
|
||||||
|
t.Printf(" Home:\t%s\n", config.Container.Home)
|
||||||
|
}
|
||||||
|
if config.Container.Hostname != "" {
|
||||||
|
t.Printf(" Hostname:\t%s\n", config.Container.Hostname)
|
||||||
|
}
|
||||||
if config.Container.Path != nil {
|
if config.Container.Path != nil {
|
||||||
t.Printf(" Path:\t%s\n", config.Container.Path)
|
t.Printf(" Path:\t%s\n", config.Container.Path)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,9 +64,9 @@ func TestPrintShowInstance(t *testing.T) {
|
|||||||
Identity: 9 (org.chromium.Chromium)
|
Identity: 9 (org.chromium.Chromium)
|
||||||
Enablements: wayland, dbus, pulseaudio
|
Enablements: wayland, dbus, pulseaudio
|
||||||
Groups: video, dialout, plugdev
|
Groups: video, dialout, plugdev
|
||||||
|
Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir
|
||||||
Home: /data/data/org.chromium.Chromium
|
Home: /data/data/org.chromium.Chromium
|
||||||
Hostname: localhost
|
Hostname: localhost
|
||||||
Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir
|
|
||||||
Path: /run/current-system/sw/bin/chromium
|
Path: /run/current-system/sw/bin/chromium
|
||||||
Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland
|
Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland
|
||||||
|
|
||||||
@ -161,9 +161,9 @@ App
|
|||||||
Identity: 9 (org.chromium.Chromium)
|
Identity: 9 (org.chromium.Chromium)
|
||||||
Enablements: wayland, dbus, pulseaudio
|
Enablements: wayland, dbus, pulseaudio
|
||||||
Groups: video, dialout, plugdev
|
Groups: video, dialout, plugdev
|
||||||
|
Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir
|
||||||
Home: /data/data/org.chromium.Chromium
|
Home: /data/data/org.chromium.Chromium
|
||||||
Hostname: localhost
|
Hostname: localhost
|
||||||
Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir
|
|
||||||
Path: /run/current-system/sw/bin/chromium
|
Path: /run/current-system/sw/bin/chromium
|
||||||
Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland
|
Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland
|
||||||
|
|
||||||
|
|||||||
@ -10,11 +10,11 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/internal"
|
"hakurei.app/internal/info"
|
||||||
"hakurei.app/message"
|
"hakurei.app/message"
|
||||||
)
|
)
|
||||||
|
|
||||||
var hakureiPathVal = internal.MustHakureiPath().String()
|
var hakureiPathVal = info.MustHakureiPath().String()
|
||||||
|
|
||||||
func mustRunApp(ctx context.Context, msg message.Msg, config *hst.Config, beforeFail func()) {
|
func mustRunApp(ctx context.Context, msg message.Msg, config *hst.Config, beforeFail func()) {
|
||||||
var (
|
var (
|
||||||
|
|||||||
@ -56,7 +56,7 @@ func NewAbs(pathname string) (*Absolute, error) {
|
|||||||
// MustAbs calls [NewAbs] and panics on error.
|
// MustAbs calls [NewAbs] and panics on error.
|
||||||
func MustAbs(pathname string) *Absolute {
|
func MustAbs(pathname string) *Absolute {
|
||||||
if a, err := NewAbs(pathname); err != nil {
|
if a, err := NewAbs(pathname); err != nil {
|
||||||
panic(err.Error())
|
panic(err)
|
||||||
} else {
|
} else {
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,8 +14,10 @@ import (
|
|||||||
. "hakurei.app/container/check"
|
. "hakurei.app/container/check"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// unsafeAbs returns check.Absolute on any string value.
|
||||||
|
//
|
||||||
//go:linkname unsafeAbs hakurei.app/container/check.unsafeAbs
|
//go:linkname unsafeAbs hakurei.app/container/check.unsafeAbs
|
||||||
func unsafeAbs(_ string) *Absolute
|
func unsafeAbs(pathname string) *Absolute
|
||||||
|
|
||||||
func TestAbsoluteError(t *testing.T) {
|
func TestAbsoluteError(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
@ -82,9 +84,9 @@ func TestNewAbs(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
wantPanic := `path "etc" is not absolute`
|
wantPanic := &AbsoluteError{Pathname: "etc"}
|
||||||
|
|
||||||
if r := recover(); r != wantPanic {
|
if r := recover(); !reflect.DeepEqual(r, wantPanic) {
|
||||||
t.Errorf("MustAbs: panic = %v; want %v", r, wantPanic)
|
t.Errorf("MustAbs: panic = %v; want %v", r, wantPanic)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import (
|
|||||||
"hakurei.app/command"
|
"hakurei.app/command"
|
||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
"hakurei.app/container/check"
|
"hakurei.app/container/check"
|
||||||
|
"hakurei.app/container/fhs"
|
||||||
"hakurei.app/container/seccomp"
|
"hakurei.app/container/seccomp"
|
||||||
"hakurei.app/container/std"
|
"hakurei.app/container/std"
|
||||||
"hakurei.app/container/vfs"
|
"hakurei.app/container/vfs"
|
||||||
@ -29,6 +30,45 @@ import (
|
|||||||
"hakurei.app/message"
|
"hakurei.app/message"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Note: this package requires cgo, which is unavailable in the Go playground.
|
||||||
|
func Example() {
|
||||||
|
// Must be called early if the current process starts containers.
|
||||||
|
container.TryArgv0(nil)
|
||||||
|
|
||||||
|
// Configure the container.
|
||||||
|
z := container.New(context.Background(), nil)
|
||||||
|
z.Hostname = "hakurei-example"
|
||||||
|
z.Proc(fhs.AbsProc).Dev(fhs.AbsDev, true)
|
||||||
|
z.Stdin, z.Stdout, z.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||||
|
|
||||||
|
// Bind / for demonstration.
|
||||||
|
z.Bind(fhs.AbsRoot, fhs.AbsRoot, 0)
|
||||||
|
if name, err := exec.LookPath("hostname"); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
z.Path = check.MustAbs(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This completes the first stage of container setup and starts the container init process.
|
||||||
|
// The new process blocks until the Serve method is called.
|
||||||
|
if err := z.Start(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This serves the setup payload to the container init process,
|
||||||
|
// starting the second stage of container setup.
|
||||||
|
if err := z.Serve(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be called if the Start method succeeds.
|
||||||
|
if err := z.Wait(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output: hakurei-example
|
||||||
|
}
|
||||||
|
|
||||||
func TestStartError(t *testing.T) {
|
func TestStartError(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@ -722,12 +762,14 @@ func TestMain(m *testing.M) {
|
|||||||
func helperNewContainerLibPaths(ctx context.Context, libPaths *[]*check.Absolute, args ...string) (c *container.Container) {
|
func helperNewContainerLibPaths(ctx context.Context, libPaths *[]*check.Absolute, args ...string) (c *container.Container) {
|
||||||
msg := message.New(nil)
|
msg := message.New(nil)
|
||||||
msg.SwapVerbose(testing.Verbose())
|
msg.SwapVerbose(testing.Verbose())
|
||||||
|
executable := check.MustAbs(container.MustExecutable(msg))
|
||||||
|
|
||||||
c = container.NewCommand(ctx, msg, absHelperInnerPath, "helper", args...)
|
c = container.NewCommand(ctx, msg, absHelperInnerPath, "helper", args...)
|
||||||
c.Env = append(c.Env, envDoCheck+"=1")
|
c.Env = append(c.Env, envDoCheck+"=1")
|
||||||
c.Bind(check.MustAbs(os.Args[0]), absHelperInnerPath, 0)
|
c.Bind(executable, absHelperInnerPath, 0)
|
||||||
|
|
||||||
// in case test has cgo enabled
|
// in case test has cgo enabled
|
||||||
if entries, err := ldd.Exec(ctx, msg, os.Args[0]); err != nil {
|
if entries, err := ldd.Resolve(ctx, msg, executable); err != nil {
|
||||||
log.Fatalf("ldd: %v", err)
|
log.Fatalf("ldd: %v", err)
|
||||||
} else {
|
} else {
|
||||||
*libPaths = ldd.Path(entries)
|
*libPaths = ldd.Path(entries)
|
||||||
|
|||||||
@ -8,8 +8,10 @@ import (
|
|||||||
|
|
||||||
/* constants in this file bypass abs check, be extremely careful when changing them! */
|
/* constants in this file bypass abs check, be extremely careful when changing them! */
|
||||||
|
|
||||||
|
// unsafeAbs returns check.Absolute on any string value.
|
||||||
|
//
|
||||||
//go:linkname unsafeAbs hakurei.app/container/check.unsafeAbs
|
//go:linkname unsafeAbs hakurei.app/container/check.unsafeAbs
|
||||||
func unsafeAbs(_ string) *check.Absolute
|
func unsafeAbs(pathname string) *check.Absolute
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// AbsRoot is [Root] as [check.Absolute].
|
// AbsRoot is [Root] as [check.Absolute].
|
||||||
@ -34,6 +36,8 @@ var (
|
|||||||
|
|
||||||
// AbsDev is [Dev] as [check.Absolute].
|
// AbsDev is [Dev] as [check.Absolute].
|
||||||
AbsDev = unsafeAbs(Dev)
|
AbsDev = unsafeAbs(Dev)
|
||||||
|
// AbsDevShm is [DevShm] as [check.Absolute].
|
||||||
|
AbsDevShm = unsafeAbs(DevShm)
|
||||||
// AbsProc is [Proc] as [check.Absolute].
|
// AbsProc is [Proc] as [check.Absolute].
|
||||||
AbsProc = unsafeAbs(Proc)
|
AbsProc = unsafeAbs(Proc)
|
||||||
// AbsSys is [Sys] as [check.Absolute].
|
// AbsSys is [Sys] as [check.Absolute].
|
||||||
|
|||||||
@ -29,6 +29,8 @@ const (
|
|||||||
|
|
||||||
// Dev points to the root directory for device nodes.
|
// Dev points to the root directory for device nodes.
|
||||||
Dev = "/dev/"
|
Dev = "/dev/"
|
||||||
|
// DevShm is the place for POSIX shared memory segments, as created via shm_open(3).
|
||||||
|
DevShm = "/dev/shm/"
|
||||||
// Proc points to a virtual kernel file system exposing the process list and other functionality.
|
// Proc points to a virtual kernel file system exposing the process list and other functionality.
|
||||||
Proc = "/proc/"
|
Proc = "/proc/"
|
||||||
// ProcSys points to a hierarchy below /proc/ that exposes a number of kernel tunables.
|
// ProcSys points to a hierarchy below /proc/ that exposes a number of kernel tunables.
|
||||||
|
|||||||
@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
#define LEN(arr) (sizeof(arr) / sizeof((arr)[0]))
|
#define LEN(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||||
|
|
||||||
int32_t hakurei_scmp_make_filter(int *ret_p, uintptr_t allocate_p,
|
int32_t hakurei_scmp_make_filter(
|
||||||
|
int *ret_p, uintptr_t allocate_p,
|
||||||
uint32_t arch, uint32_t multiarch,
|
uint32_t arch, uint32_t multiarch,
|
||||||
struct hakurei_syscall_rule *rules,
|
struct hakurei_syscall_rule *rules,
|
||||||
size_t rules_sz, hakurei_export_flag flags) {
|
size_t rules_sz, hakurei_export_flag flags) {
|
||||||
@ -72,11 +73,9 @@ int32_t hakurei_scmp_make_filter(int *ret_p, uintptr_t allocate_p,
|
|||||||
assert(rule->m_errno == EPERM || rule->m_errno == ENOSYS);
|
assert(rule->m_errno == EPERM || rule->m_errno == ENOSYS);
|
||||||
|
|
||||||
if (rule->arg)
|
if (rule->arg)
|
||||||
*ret_p = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(rule->m_errno),
|
*ret_p = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(rule->m_errno), rule->syscall, 1, *rule->arg);
|
||||||
rule->syscall, 1, *rule->arg);
|
|
||||||
else
|
else
|
||||||
*ret_p = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(rule->m_errno),
|
*ret_p = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(rule->m_errno), rule->syscall, 0);
|
||||||
rule->syscall, 0);
|
|
||||||
|
|
||||||
if (*ret_p == -EFAULT) {
|
if (*ret_p == -EFAULT) {
|
||||||
res = 4;
|
res = 4;
|
||||||
@ -93,22 +92,17 @@ int32_t hakurei_scmp_make_filter(int *ret_p, uintptr_t allocate_p,
|
|||||||
last_allowed_family = -1;
|
last_allowed_family = -1;
|
||||||
for (i = 0; i < LEN(socket_family_allowlist); i++) {
|
for (i = 0; i < LEN(socket_family_allowlist); i++) {
|
||||||
if (socket_family_allowlist[i].flags_mask != 0 &&
|
if (socket_family_allowlist[i].flags_mask != 0 &&
|
||||||
(socket_family_allowlist[i].flags_mask & flags) !=
|
(socket_family_allowlist[i].flags_mask & flags) != socket_family_allowlist[i].flags_mask)
|
||||||
socket_family_allowlist[i].flags_mask)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (disallowed = last_allowed_family + 1;
|
for (disallowed = last_allowed_family + 1; disallowed < socket_family_allowlist[i].family; disallowed++) {
|
||||||
disallowed < socket_family_allowlist[i].family; disallowed++) {
|
|
||||||
/* Blocklist the in-between valid families */
|
/* Blocklist the in-between valid families */
|
||||||
seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EAFNOSUPPORT),
|
seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EAFNOSUPPORT), SCMP_SYS(socket), 1, SCMP_A0(SCMP_CMP_EQ, disallowed));
|
||||||
SCMP_SYS(socket), 1,
|
|
||||||
SCMP_A0(SCMP_CMP_EQ, disallowed));
|
|
||||||
}
|
}
|
||||||
last_allowed_family = socket_family_allowlist[i].family;
|
last_allowed_family = socket_family_allowlist[i].family;
|
||||||
}
|
}
|
||||||
/* Blocklist the rest */
|
/* Blocklist the rest */
|
||||||
seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EAFNOSUPPORT), SCMP_SYS(socket), 1,
|
seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EAFNOSUPPORT), SCMP_SYS(socket), 1, SCMP_A0(SCMP_CMP_GE, last_allowed_family + 1));
|
||||||
SCMP_A0(SCMP_CMP_GE, last_allowed_family + 1));
|
|
||||||
|
|
||||||
if (allocate_p == 0) {
|
if (allocate_p == 0) {
|
||||||
*ret_p = seccomp_load(ctx);
|
*ret_p = seccomp_load(ctx);
|
||||||
|
|||||||
@ -19,7 +19,8 @@ struct hakurei_syscall_rule {
|
|||||||
};
|
};
|
||||||
|
|
||||||
extern void *hakurei_scmp_allocate(uintptr_t f, size_t len);
|
extern void *hakurei_scmp_allocate(uintptr_t f, size_t len);
|
||||||
int32_t hakurei_scmp_make_filter(int *ret_p, uintptr_t allocate_p,
|
int32_t hakurei_scmp_make_filter(
|
||||||
|
int *ret_p, uintptr_t allocate_p,
|
||||||
uint32_t arch, uint32_t multiarch,
|
uint32_t arch, uint32_t multiarch,
|
||||||
struct hakurei_syscall_rule *rules,
|
struct hakurei_syscall_rule *rules,
|
||||||
size_t rules_sz, hakurei_export_flag flags);
|
size_t rules_sz, hakurei_export_flag flags);
|
||||||
@ -7,8 +7,10 @@ import (
|
|||||||
"hakurei.app/container/stub"
|
"hakurei.app/container/stub"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Made available here to check panic recovery behaviour.
|
||||||
|
//
|
||||||
//go:linkname handleExitNew hakurei.app/container/stub.handleExitNew
|
//go:linkname handleExitNew hakurei.app/container/stub.handleExitNew
|
||||||
func handleExitNew(_ testing.TB)
|
func handleExitNew(t testing.TB)
|
||||||
|
|
||||||
// overrideTFailNow overrides the Fail and FailNow method.
|
// overrideTFailNow overrides the Fail and FailNow method.
|
||||||
type overrideTFailNow struct {
|
type overrideTFailNow struct {
|
||||||
|
|||||||
6
dist/release.sh
vendored
6
dist/release.sh
vendored
@ -10,9 +10,9 @@ cp -rv "dist/comp" "${out}"
|
|||||||
|
|
||||||
go generate ./...
|
go generate ./...
|
||||||
go build -trimpath -v -o "${out}/bin/" -ldflags "-s -w -buildid= -extldflags '-static'
|
go build -trimpath -v -o "${out}/bin/" -ldflags "-s -w -buildid= -extldflags '-static'
|
||||||
-X hakurei.app/internal.buildVersion=${VERSION}
|
-X hakurei.app/internal/info.buildVersion=${VERSION}
|
||||||
-X hakurei.app/internal.hakureiPath=/usr/bin/hakurei
|
-X hakurei.app/internal/info.hakureiPath=/usr/bin/hakurei
|
||||||
-X hakurei.app/internal.hsuPath=/usr/bin/hsu
|
-X hakurei.app/internal/info.hsuPath=/usr/bin/hsu
|
||||||
-X main.hakureiPath=/usr/bin/hakurei" ./...
|
-X main.hakureiPath=/usr/bin/hakurei" ./...
|
||||||
|
|
||||||
rm -f "./${out}.tar.gz" && tar -C dist -czf "${out}.tar.gz" "${pname}"
|
rm -f "./${out}.tar.gz" && tar -C dist -czf "${out}.tar.gz" "${pname}"
|
||||||
|
|||||||
@ -114,7 +114,7 @@
|
|||||||
inherit (pkgs)
|
inherit (pkgs)
|
||||||
# passthru.buildInputs
|
# passthru.buildInputs
|
||||||
go
|
go
|
||||||
gcc
|
clang
|
||||||
|
|
||||||
# nativeBuildInputs
|
# nativeBuildInputs
|
||||||
pkg-config
|
pkg-config
|
||||||
@ -129,6 +129,10 @@
|
|||||||
zstd
|
zstd
|
||||||
gnutar
|
gnutar
|
||||||
coreutils
|
coreutils
|
||||||
|
|
||||||
|
# for check
|
||||||
|
util-linux
|
||||||
|
nettools
|
||||||
;
|
;
|
||||||
};
|
};
|
||||||
hsu = pkgs.callPackage ./cmd/hsu/package.nix { inherit (self.packages.${system}) hakurei; };
|
hsu = pkgs.callPackage ./cmd/hsu/package.nix { inherit (self.packages.${system}) hakurei; };
|
||||||
@ -144,7 +148,7 @@
|
|||||||
&& chmod -R +w .
|
&& chmod -R +w .
|
||||||
|
|
||||||
export HAKUREI_VERSION="v${hakurei.version}"
|
export HAKUREI_VERSION="v${hakurei.version}"
|
||||||
./dist/release.sh && mkdir $out && cp -v "dist/hakurei-$HAKUREI_VERSION.tar.gz"* $out
|
CC="clang -O3 -Werror" ./dist/release.sh && mkdir $out && cp -v "dist/hakurei-$HAKUREI_VERSION.tar.gz"* $out
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
73
helper/deprecated.go
Normal file
73
helper/deprecated.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// Package helper exposes the internal/helper package.
|
||||||
|
//
|
||||||
|
// Deprecated: This package will be removed in 0.4.
|
||||||
|
package helper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"time"
|
||||||
|
_ "unsafe" // for go:linkname
|
||||||
|
|
||||||
|
"hakurei.app/container"
|
||||||
|
"hakurei.app/container/check"
|
||||||
|
"hakurei.app/internal/helper"
|
||||||
|
"hakurei.app/message"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname WaitDelay hakurei.app/internal/helper.WaitDelay
|
||||||
|
var WaitDelay time.Duration
|
||||||
|
|
||||||
|
const (
|
||||||
|
// HakureiHelper is set to 1 when args fd is enabled and 0 otherwise.
|
||||||
|
HakureiHelper = helper.HakureiHelper
|
||||||
|
// HakureiStatus is set to 1 when stat fd is enabled and 0 otherwise.
|
||||||
|
HakureiStatus = helper.HakureiStatus
|
||||||
|
)
|
||||||
|
|
||||||
|
type Helper = helper.Helper
|
||||||
|
|
||||||
|
// NewCheckedArgs returns a checked null-terminated argument writer for a copy of args.
|
||||||
|
//
|
||||||
|
//go:linkname NewCheckedArgs hakurei.app/internal/helper.NewCheckedArgs
|
||||||
|
func NewCheckedArgs(args ...string) (wt io.WriterTo, err error)
|
||||||
|
|
||||||
|
// MustNewCheckedArgs returns a checked null-terminated argument writer for a copy of args.
|
||||||
|
// If s contains a NUL byte this function panics instead of returning an error.
|
||||||
|
//
|
||||||
|
//go:linkname MustNewCheckedArgs hakurei.app/internal/helper.MustNewCheckedArgs
|
||||||
|
func MustNewCheckedArgs(args ...string) io.WriterTo
|
||||||
|
|
||||||
|
// NewDirect initialises a new direct Helper instance with wt as the null-terminated argument writer.
|
||||||
|
// Function argF returns an array of arguments passed directly to the child process.
|
||||||
|
//
|
||||||
|
//go:linkname NewDirect hakurei.app/internal/helper.NewDirect
|
||||||
|
func NewDirect(
|
||||||
|
ctx context.Context,
|
||||||
|
name string,
|
||||||
|
wt io.WriterTo,
|
||||||
|
stat bool,
|
||||||
|
argF func(argsFd, statFd int) []string,
|
||||||
|
cmdF func(cmd *exec.Cmd),
|
||||||
|
extraFiles []*os.File,
|
||||||
|
) Helper
|
||||||
|
|
||||||
|
// New initialises a Helper instance with wt as the null-terminated argument writer.
|
||||||
|
//
|
||||||
|
//go:linkname New hakurei.app/internal/helper.New
|
||||||
|
func New(
|
||||||
|
ctx context.Context,
|
||||||
|
msg message.Msg,
|
||||||
|
pathname *check.Absolute, name string,
|
||||||
|
wt io.WriterTo,
|
||||||
|
stat bool,
|
||||||
|
argF func(argsFd, statFd int) []string,
|
||||||
|
cmdF func(z *container.Container),
|
||||||
|
extraFiles []*os.File,
|
||||||
|
) Helper
|
||||||
|
|
||||||
|
// InternalHelperStub is an internal function but exported because it is cross-package;
|
||||||
|
// it is part of the implementation of the helper stub.
|
||||||
|
func InternalHelperStub() { helper.InternalHelperStub() }
|
||||||
63
helper/proc/deprecated.go
Normal file
63
helper/proc/deprecated.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Deprecated: This package will be removed in 0.4.
|
||||||
|
package proc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"time"
|
||||||
|
_ "unsafe" // for go:linkname
|
||||||
|
|
||||||
|
"hakurei.app/internal/helper/proc"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname FulfillmentTimeout hakurei.app/internal/helper/proc.FulfillmentTimeout
|
||||||
|
var FulfillmentTimeout time.Duration
|
||||||
|
|
||||||
|
// A File is an extra file with deferred initialisation.
|
||||||
|
type File = proc.File
|
||||||
|
|
||||||
|
// ExtraFilesPre is a linked list storing addresses of [os.File].
|
||||||
|
type ExtraFilesPre = proc.ExtraFilesPre
|
||||||
|
|
||||||
|
// Fulfill calls the [File.Fulfill] method on all files, starts cmd and blocks until all fulfillment completes.
|
||||||
|
//
|
||||||
|
//go:linkname Fulfill hakurei.app/internal/helper/proc.Fulfill
|
||||||
|
func Fulfill(ctx context.Context,
|
||||||
|
v *[]*os.File, start func() error,
|
||||||
|
files []File, extraFiles *ExtraFilesPre,
|
||||||
|
) (err error)
|
||||||
|
|
||||||
|
// InitFile initialises f as part of the slice extraFiles points to,
|
||||||
|
// and returns its final fd value.
|
||||||
|
//
|
||||||
|
//go:linkname InitFile hakurei.app/internal/helper/proc.InitFile
|
||||||
|
func InitFile(f File, extraFiles *ExtraFilesPre) (fd uintptr)
|
||||||
|
|
||||||
|
// BaseFile implements the Init method of the File interface and provides indirect access to extra file state.
|
||||||
|
type BaseFile = proc.BaseFile
|
||||||
|
|
||||||
|
//go:linkname ExtraFile hakurei.app/internal/helper/proc.ExtraFile
|
||||||
|
func ExtraFile(cmd *exec.Cmd, f *os.File) (fd uintptr)
|
||||||
|
|
||||||
|
//go:linkname ExtraFileSlice hakurei.app/internal/helper/proc.ExtraFileSlice
|
||||||
|
func ExtraFileSlice(extraFiles *[]*os.File, f *os.File) (fd uintptr)
|
||||||
|
|
||||||
|
// NewWriterTo returns a [File] that receives content from wt on fulfillment.
|
||||||
|
//
|
||||||
|
//go:linkname NewWriterTo hakurei.app/internal/helper/proc.NewWriterTo
|
||||||
|
func NewWriterTo(wt io.WriterTo) File
|
||||||
|
|
||||||
|
// NewStat returns a [File] implementing the behaviour
|
||||||
|
// of the receiving end of xdg-dbus-proxy stat fd.
|
||||||
|
//
|
||||||
|
//go:linkname NewStat hakurei.app/internal/helper/proc.NewStat
|
||||||
|
func NewStat(s *io.Closer) File
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:linkname ErrStatFault hakurei.app/internal/helper/proc.ErrStatFault
|
||||||
|
ErrStatFault error
|
||||||
|
//go:linkname ErrStatRead hakurei.app/internal/helper/proc.ErrStatRead
|
||||||
|
ErrStatRead error
|
||||||
|
)
|
||||||
@ -6,11 +6,13 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
_ "unsafe"
|
_ "unsafe" // for go:linkname
|
||||||
|
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Made available here to check time encoding behaviour of [hst.ID].
|
||||||
|
//
|
||||||
//go:linkname newInstanceID hakurei.app/hst.newInstanceID
|
//go:linkname newInstanceID hakurei.app/hst.newInstanceID
|
||||||
func newInstanceID(id *hst.ID, p uint64) error
|
func newInstanceID(id *hst.ID, p uint64) error
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/system/acl"
|
"hakurei.app/internal/acl"
|
||||||
)
|
)
|
||||||
|
|
||||||
const testFileName = "acl.test"
|
const testFileName = "acl.test"
|
||||||
90
internal/acl/libacl-helper.c
Normal file
90
internal/acl/libacl-helper.c
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#include "libacl-helper.h"
|
||||||
|
#include <acl/libacl.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/acl.h>
|
||||||
|
|
||||||
|
int hakurei_acl_update_file_by_uid(const char *path_p, uid_t uid,
|
||||||
|
acl_perm_t *perms, size_t plen) {
|
||||||
|
int ret;
|
||||||
|
bool v;
|
||||||
|
int i;
|
||||||
|
acl_t acl;
|
||||||
|
acl_entry_t entry;
|
||||||
|
acl_tag_t tag_type;
|
||||||
|
void *qualifier_p;
|
||||||
|
acl_permset_t permset;
|
||||||
|
|
||||||
|
ret = -1; /* acl_get_file */
|
||||||
|
acl = acl_get_file(path_p, ACL_TYPE_ACCESS);
|
||||||
|
if (acl == NULL)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* prune entries by uid */
|
||||||
|
for (i = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); i == 1;
|
||||||
|
i = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry)) {
|
||||||
|
ret = -2; /* acl_get_tag_type */
|
||||||
|
if (acl_get_tag_type(entry, &tag_type) != 0)
|
||||||
|
goto out;
|
||||||
|
if (tag_type != ACL_USER)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = -3; /* acl_get_qualifier */
|
||||||
|
qualifier_p = acl_get_qualifier(entry);
|
||||||
|
if (qualifier_p == NULL)
|
||||||
|
goto out;
|
||||||
|
v = *(uid_t *)qualifier_p == uid;
|
||||||
|
acl_free(qualifier_p);
|
||||||
|
|
||||||
|
if (!v)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = -4; /* acl_delete_entry */
|
||||||
|
if (acl_delete_entry(acl, entry) != 0)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plen == 0)
|
||||||
|
goto set;
|
||||||
|
|
||||||
|
ret = -5; /* acl_create_entry */
|
||||||
|
if (acl_create_entry(&acl, &entry) != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = -6; /* acl_get_permset */
|
||||||
|
if (acl_get_permset(entry, &permset) != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = -7; /* acl_add_perm */
|
||||||
|
for (i = 0; i < plen; i++) {
|
||||||
|
if (acl_add_perm(permset, perms[i]) != 0)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = -8; /* acl_set_tag_type */
|
||||||
|
if (acl_set_tag_type(entry, ACL_USER) != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = -9; /* acl_set_qualifier */
|
||||||
|
if (acl_set_qualifier(entry, (void *)&uid) != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
set:
|
||||||
|
ret = -10; /* acl_calc_mask */
|
||||||
|
if (acl_calc_mask(&acl) != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = -11; /* acl_valid */
|
||||||
|
if (acl_valid(acl) != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = -12; /* acl_set_file */
|
||||||
|
if (acl_set_file(path_p, ACL_TYPE_ACCESS, acl) == 0)
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
free((void *)path_p);
|
||||||
|
if (acl != NULL)
|
||||||
|
acl_free((void *)acl);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@ package acl_test
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/system/acl"
|
"hakurei.app/internal/acl"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPerms(t *testing.T) {
|
func TestPerms(t *testing.T) {
|
||||||
@ -5,7 +5,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/system/dbus"
|
"hakurei.app/internal/dbus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParse(t *testing.T) {
|
func TestParse(t *testing.T) {
|
||||||
@ -7,7 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/system/dbus"
|
"hakurei.app/internal/dbus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConfigArgs(t *testing.T) {
|
func TestConfigArgs(t *testing.T) {
|
||||||
@ -11,9 +11,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/helper"
|
"hakurei.app/internal/dbus"
|
||||||
|
"hakurei.app/internal/helper"
|
||||||
"hakurei.app/message"
|
"hakurei.app/message"
|
||||||
"hakurei.app/system/dbus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFinalise(t *testing.T) {
|
func TestFinalise(t *testing.T) {
|
||||||
@ -12,7 +12,7 @@ import (
|
|||||||
"hakurei.app/container/check"
|
"hakurei.app/container/check"
|
||||||
"hakurei.app/container/seccomp"
|
"hakurei.app/container/seccomp"
|
||||||
"hakurei.app/container/std"
|
"hakurei.app/container/std"
|
||||||
"hakurei.app/helper"
|
"hakurei.app/internal/helper"
|
||||||
"hakurei.app/ldd"
|
"hakurei.app/ldd"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ func (p *Proxy) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var libPaths []*check.Absolute
|
var libPaths []*check.Absolute
|
||||||
if entries, err := ldd.Exec(ctx, p.msg, toolPath.String()); err != nil {
|
if entries, err := ldd.Resolve(ctx, p.msg, toolPath); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
libPaths = ldd.Path(entries)
|
libPaths = ldd.Path(entries)
|
||||||
@ -5,7 +5,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
"hakurei.app/helper"
|
"hakurei.app/internal/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) { container.TryArgv0(nil); helper.InternalHelperStub(); os.Exit(m.Run()) }
|
func TestMain(m *testing.M) { container.TryArgv0(nil); helper.InternalHelperStub(); os.Exit(m.Run()) }
|
||||||
@ -6,8 +6,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"hakurei.app/helper"
|
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
|
"hakurei.app/internal/helper"
|
||||||
"hakurei.app/message"
|
"hakurei.app/message"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/helper"
|
"hakurei.app/internal/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestArgsString(t *testing.T) {
|
func TestArgsString(t *testing.T) {
|
||||||
@ -10,7 +10,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"hakurei.app/helper/proc"
|
"hakurei.app/internal/helper/proc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewDirect initialises a new direct Helper instance with wt as the null-terminated argument writer.
|
// NewDirect initialises a new direct Helper instance with wt as the null-terminated argument writer.
|
||||||
@ -9,7 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
"hakurei.app/helper"
|
"hakurei.app/internal/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCmd(t *testing.T) {
|
func TestCmd(t *testing.T) {
|
||||||
@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
"hakurei.app/container/check"
|
"hakurei.app/container/check"
|
||||||
"hakurei.app/helper/proc"
|
"hakurei.app/internal/helper/proc"
|
||||||
"hakurei.app/message"
|
"hakurei.app/message"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ import (
|
|||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
"hakurei.app/container/check"
|
"hakurei.app/container/check"
|
||||||
"hakurei.app/container/fhs"
|
"hakurei.app/container/fhs"
|
||||||
"hakurei.app/helper"
|
"hakurei.app/internal/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestContainer(t *testing.T) {
|
func TestContainer(t *testing.T) {
|
||||||
@ -8,7 +8,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/helper/proc"
|
"hakurei.app/internal/helper/proc"
|
||||||
)
|
)
|
||||||
|
|
||||||
var WaitDelay = 2 * time.Second
|
var WaitDelay = 2 * time.Second
|
||||||
@ -13,7 +13,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/helper"
|
"hakurei.app/internal/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -5,7 +5,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
"hakurei.app/helper"
|
"hakurei.app/internal/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) { container.TryArgv0(nil); helper.InternalHelperStub(); os.Exit(m.Run()) }
|
func TestMain(m *testing.M) { container.TryArgv0(nil); helper.InternalHelperStub(); os.Exit(m.Run()) }
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package internal
|
package info
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package internal
|
package info
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package internal
|
package info
|
||||||
|
|
||||||
// FallbackVersion is returned when a version string was not set by the linker.
|
// FallbackVersion is returned when a version string was not set by the linker.
|
||||||
const FallbackVersion = "dirty"
|
const FallbackVersion = "dirty"
|
||||||
@ -14,9 +14,9 @@ import (
|
|||||||
"hakurei.app/container/check"
|
"hakurei.app/container/check"
|
||||||
"hakurei.app/container/seccomp"
|
"hakurei.app/container/seccomp"
|
||||||
"hakurei.app/container/std"
|
"hakurei.app/container/std"
|
||||||
"hakurei.app/internal"
|
"hakurei.app/internal/dbus"
|
||||||
|
"hakurei.app/internal/info"
|
||||||
"hakurei.app/message"
|
"hakurei.app/message"
|
||||||
"hakurei.app/system/dbus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// osFile represents [os.File].
|
// osFile represents [os.File].
|
||||||
@ -156,7 +156,7 @@ func (direct) seccompLoad(rules []std.NativeRule, flags seccomp.ExportFlag) erro
|
|||||||
return seccomp.Load(rules, flags)
|
return seccomp.Load(rules, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (direct) mustHsuPath() *check.Absolute { return internal.MustHsuPath() }
|
func (direct) mustHsuPath() *check.Absolute { return info.MustHsuPath() }
|
||||||
|
|
||||||
func (direct) dbusAddress() (session, system string) { return dbus.Address() }
|
func (direct) dbusAddress() (session, system string) { return dbus.Address() }
|
||||||
|
|
||||||
|
|||||||
@ -24,8 +24,8 @@ import (
|
|||||||
"hakurei.app/container/std"
|
"hakurei.app/container/std"
|
||||||
"hakurei.app/container/stub"
|
"hakurei.app/container/stub"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
|
"hakurei.app/internal/system"
|
||||||
"hakurei.app/message"
|
"hakurei.app/message"
|
||||||
"hakurei.app/system"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// call initialises a [stub.Call].
|
// call initialises a [stub.Call].
|
||||||
|
|||||||
@ -8,8 +8,8 @@ import (
|
|||||||
"os/user"
|
"os/user"
|
||||||
|
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
|
"hakurei.app/internal/system"
|
||||||
"hakurei.app/message"
|
"hakurei.app/message"
|
||||||
"hakurei.app/system"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func newWithMessage(msg string) error { return newWithMessageError(msg, os.ErrInvalid) }
|
func newWithMessage(msg string) error { return newWithMessageError(msg, os.ErrInvalid) }
|
||||||
|
|||||||
@ -9,10 +9,10 @@ import (
|
|||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
"hakurei.app/container/check"
|
"hakurei.app/container/check"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
|
"hakurei.app/internal/acl"
|
||||||
"hakurei.app/internal/env"
|
"hakurei.app/internal/env"
|
||||||
|
"hakurei.app/internal/system"
|
||||||
"hakurei.app/message"
|
"hakurei.app/message"
|
||||||
"hakurei.app/system"
|
|
||||||
"hakurei.app/system/acl"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// envAllocSize is the initial size of the env map pre-allocated when the configured env map is nil.
|
// envAllocSize is the initial size of the env map pre-allocated when the configured env map is nil.
|
||||||
|
|||||||
@ -16,10 +16,10 @@ import (
|
|||||||
"hakurei.app/container/check"
|
"hakurei.app/container/check"
|
||||||
"hakurei.app/container/fhs"
|
"hakurei.app/container/fhs"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/internal"
|
"hakurei.app/internal/info"
|
||||||
"hakurei.app/internal/store"
|
"hakurei.app/internal/store"
|
||||||
|
"hakurei.app/internal/system"
|
||||||
"hakurei.app/message"
|
"hakurei.app/message"
|
||||||
"hakurei.app/system"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -39,7 +39,7 @@ func (k *outcome) main(msg message.Msg, identifierFd int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// read comp value early for early failure
|
// read comp value early for early failure
|
||||||
hsuPath := internal.MustHsuPath()
|
hsuPath := info.MustHsuPath()
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// transitions to processCommit, or processFinal on failure
|
// transitions to processCommit, or processFinal on failure
|
||||||
|
|||||||
@ -12,6 +12,8 @@ import (
|
|||||||
|
|
||||||
// IsPollDescriptor reports whether fd is the descriptor being used by the poller.
|
// IsPollDescriptor reports whether fd is the descriptor being used by the poller.
|
||||||
//
|
//
|
||||||
|
// Made available here to determine and reject impossible fd.
|
||||||
|
//
|
||||||
//go:linkname IsPollDescriptor internal/poll.IsPollDescriptor
|
//go:linkname IsPollDescriptor internal/poll.IsPollDescriptor
|
||||||
func IsPollDescriptor(fd uintptr) bool
|
func IsPollDescriptor(fd uintptr) bool
|
||||||
|
|
||||||
@ -21,10 +21,10 @@ import (
|
|||||||
"hakurei.app/container/seccomp"
|
"hakurei.app/container/seccomp"
|
||||||
"hakurei.app/container/std"
|
"hakurei.app/container/std"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
|
"hakurei.app/internal/acl"
|
||||||
|
"hakurei.app/internal/dbus"
|
||||||
|
"hakurei.app/internal/system"
|
||||||
"hakurei.app/message"
|
"hakurei.app/message"
|
||||||
"hakurei.app/system"
|
|
||||||
"hakurei.app/system/acl"
|
|
||||||
"hakurei.app/system/dbus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestOutcomeMain(t *testing.T) {
|
func TestOutcomeMain(t *testing.T) {
|
||||||
@ -141,7 +141,7 @@ func TestOutcomeMain(t *testing.T) {
|
|||||||
Proc(fhs.AbsProc).
|
Proc(fhs.AbsProc).
|
||||||
Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
|
Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
|
||||||
Bind(fhs.AbsDev, fhs.AbsDev, std.BindWritable|std.BindDevice).
|
Bind(fhs.AbsDev, fhs.AbsDev, std.BindWritable|std.BindDevice).
|
||||||
Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777).
|
Tmpfs(fhs.AbsDevShm, 0, 01777).
|
||||||
|
|
||||||
// spRuntimeOp
|
// spRuntimeOp
|
||||||
Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
|
Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
|
||||||
@ -243,7 +243,7 @@ func TestOutcomeMain(t *testing.T) {
|
|||||||
Proc(m("/proc/")).
|
Proc(m("/proc/")).
|
||||||
Tmpfs(hst.AbsPrivateTmp, 4096, 0755).
|
Tmpfs(hst.AbsPrivateTmp, 4096, 0755).
|
||||||
DevWritable(m("/dev/"), true).
|
DevWritable(m("/dev/"), true).
|
||||||
Tmpfs(m("/dev/shm"), 0, 01777).
|
Tmpfs(m("/dev/shm/"), 0, 01777).
|
||||||
Tmpfs(m("/run/user/"), 4096, 0755).
|
Tmpfs(m("/run/user/"), 4096, 0755).
|
||||||
Bind(m("/tmp/hakurei.0/runtime/0"), m("/run/user/65534"), std.BindWritable).
|
Bind(m("/tmp/hakurei.0/runtime/0"), m("/run/user/65534"), std.BindWritable).
|
||||||
Bind(m("/tmp/hakurei.0/tmpdir/0"), m("/tmp/"), std.BindWritable).
|
Bind(m("/tmp/hakurei.0/tmpdir/0"), m("/tmp/"), std.BindWritable).
|
||||||
@ -412,7 +412,7 @@ func TestOutcomeMain(t *testing.T) {
|
|||||||
Proc(m("/proc/")).
|
Proc(m("/proc/")).
|
||||||
Tmpfs(hst.AbsPrivateTmp, 4096, 0755).
|
Tmpfs(hst.AbsPrivateTmp, 4096, 0755).
|
||||||
DevWritable(m("/dev/"), true).
|
DevWritable(m("/dev/"), true).
|
||||||
Tmpfs(m("/dev/shm"), 0, 01777).
|
Tmpfs(m("/dev/shm/"), 0, 01777).
|
||||||
Tmpfs(m("/run/user/"), 4096, 0755).
|
Tmpfs(m("/run/user/"), 4096, 0755).
|
||||||
Bind(m("/tmp/hakurei.0/runtime/9"), m("/run/user/65534"), std.BindWritable).
|
Bind(m("/tmp/hakurei.0/runtime/9"), m("/run/user/65534"), std.BindWritable).
|
||||||
Bind(m("/tmp/hakurei.0/tmpdir/9"), m("/tmp/"), std.BindWritable).
|
Bind(m("/tmp/hakurei.0/tmpdir/9"), m("/tmp/"), std.BindWritable).
|
||||||
@ -558,7 +558,7 @@ func TestOutcomeMain(t *testing.T) {
|
|||||||
Proc(m("/proc/")).
|
Proc(m("/proc/")).
|
||||||
Tmpfs(hst.AbsPrivateTmp, 4096, 0755).
|
Tmpfs(hst.AbsPrivateTmp, 4096, 0755).
|
||||||
DevWritable(m("/dev/"), true).
|
DevWritable(m("/dev/"), true).
|
||||||
Tmpfs(m("/dev/shm"), 0, 01777).
|
Tmpfs(m("/dev/shm/"), 0, 01777).
|
||||||
Tmpfs(m("/run/user/"), 4096, 0755).
|
Tmpfs(m("/run/user/"), 4096, 0755).
|
||||||
Bind(m("/tmp/hakurei.0/runtime/1"), m("/run/user/1971"), std.BindWritable).
|
Bind(m("/tmp/hakurei.0/runtime/1"), m("/run/user/1971"), std.BindWritable).
|
||||||
Bind(m("/tmp/hakurei.0/tmpdir/1"), m("/tmp/"), std.BindWritable).
|
Bind(m("/tmp/hakurei.0/tmpdir/1"), m("/tmp/"), std.BindWritable).
|
||||||
@ -38,7 +38,7 @@ static void hakurei_shim_sigaction(int sig, siginfo_t *si, void *ucontext) {
|
|||||||
|
|
||||||
void hakurei_shim_setup_cont_signal(pid_t ppid, int fd) {
|
void hakurei_shim_setup_cont_signal(pid_t ppid, int fd) {
|
||||||
if (hakurei_shim_param_ppid != -1 || hakurei_shim_fd != -1)
|
if (hakurei_shim_param_ppid != -1 || hakurei_shim_fd != -1)
|
||||||
*(int *)NULL = 0; /* unreachable */
|
*(volatile int *)NULL = 0; /* unreachable */
|
||||||
|
|
||||||
struct sigaction new_action = {0}, old_action = {0};
|
struct sigaction new_action = {0}, old_action = {0};
|
||||||
if (sigaction(SIGCONT, NULL, &old_action) != 0)
|
if (sigaction(SIGCONT, NULL, &old_action) != 0)
|
||||||
|
|||||||
@ -66,7 +66,7 @@ func TestShimEntrypoint(t *testing.T) {
|
|||||||
Proc(fhs.AbsProc).
|
Proc(fhs.AbsProc).
|
||||||
Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
|
Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
|
||||||
Bind(fhs.AbsDev, fhs.AbsDev, std.BindWritable|std.BindDevice).
|
Bind(fhs.AbsDev, fhs.AbsDev, std.BindWritable|std.BindDevice).
|
||||||
Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777).
|
Tmpfs(fhs.AbsDevShm, 0, 01777).
|
||||||
|
|
||||||
// spRuntimeOp
|
// spRuntimeOp
|
||||||
Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
|
Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
|
||||||
|
|||||||
@ -16,11 +16,11 @@ import (
|
|||||||
"hakurei.app/container/seccomp"
|
"hakurei.app/container/seccomp"
|
||||||
"hakurei.app/container/std"
|
"hakurei.app/container/std"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
|
"hakurei.app/internal/acl"
|
||||||
|
"hakurei.app/internal/dbus"
|
||||||
|
"hakurei.app/internal/system"
|
||||||
"hakurei.app/internal/validate"
|
"hakurei.app/internal/validate"
|
||||||
"hakurei.app/message"
|
"hakurei.app/message"
|
||||||
"hakurei.app/system"
|
|
||||||
"hakurei.app/system/acl"
|
|
||||||
"hakurei.app/system/dbus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const varRunNscd = fhs.Var + "run/nscd"
|
const varRunNscd = fhs.Var + "run/nscd"
|
||||||
@ -116,7 +116,7 @@ func (s *spParamsOp) toContainer(state *outcomeStateParams) error {
|
|||||||
state.params.Bind(fhs.AbsDev, fhs.AbsDev, std.BindWritable|std.BindDevice)
|
state.params.Bind(fhs.AbsDev, fhs.AbsDev, std.BindWritable|std.BindDevice)
|
||||||
}
|
}
|
||||||
// /dev is mounted readonly later on, this prevents /dev/shm from going readonly with it
|
// /dev is mounted readonly later on, this prevents /dev/shm from going readonly with it
|
||||||
state.params.Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777)
|
state.params.Tmpfs(fhs.AbsDevShm, 0, 01777)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,9 +14,9 @@ import (
|
|||||||
"hakurei.app/container/std"
|
"hakurei.app/container/std"
|
||||||
"hakurei.app/container/stub"
|
"hakurei.app/container/stub"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/system"
|
"hakurei.app/internal/acl"
|
||||||
"hakurei.app/system/acl"
|
"hakurei.app/internal/dbus"
|
||||||
"hakurei.app/system/dbus"
|
"hakurei.app/internal/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSpParamsOp(t *testing.T) {
|
func TestSpParamsOp(t *testing.T) {
|
||||||
@ -72,7 +72,7 @@ func TestSpParamsOp(t *testing.T) {
|
|||||||
Root(m("/var/lib/hakurei/base/org.debian"), std.BindWritable).
|
Root(m("/var/lib/hakurei/base/org.debian"), std.BindWritable).
|
||||||
Proc(fhs.AbsProc).Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
|
Proc(fhs.AbsProc).Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
|
||||||
DevWritable(fhs.AbsDev, true).
|
DevWritable(fhs.AbsDev, true).
|
||||||
Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777),
|
Tmpfs(fhs.AbsDevShm, 0, 01777),
|
||||||
}, paramsWantEnv(config, map[string]string{
|
}, paramsWantEnv(config, map[string]string{
|
||||||
"TERM": "xterm",
|
"TERM": "xterm",
|
||||||
}, func(t *testing.T, state *outcomeStateParams) {
|
}, func(t *testing.T, state *outcomeStateParams) {
|
||||||
@ -110,7 +110,7 @@ func TestSpParamsOp(t *testing.T) {
|
|||||||
Root(m("/var/lib/hakurei/base/org.debian"), std.BindWritable).
|
Root(m("/var/lib/hakurei/base/org.debian"), std.BindWritable).
|
||||||
Proc(fhs.AbsProc).Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
|
Proc(fhs.AbsProc).Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
|
||||||
Bind(fhs.AbsDev, fhs.AbsDev, std.BindWritable|std.BindDevice).
|
Bind(fhs.AbsDev, fhs.AbsDev, std.BindWritable|std.BindDevice).
|
||||||
Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777),
|
Tmpfs(fhs.AbsDevShm, 0, 01777),
|
||||||
}, paramsWantEnv(config, map[string]string{
|
}, paramsWantEnv(config, map[string]string{
|
||||||
"TERM": "xterm",
|
"TERM": "xterm",
|
||||||
}, func(t *testing.T, state *outcomeStateParams) {
|
}, func(t *testing.T, state *outcomeStateParams) {
|
||||||
|
|||||||
@ -5,8 +5,8 @@ import (
|
|||||||
|
|
||||||
"hakurei.app/container/fhs"
|
"hakurei.app/container/fhs"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/system/acl"
|
"hakurei.app/internal/acl"
|
||||||
"hakurei.app/system/dbus"
|
"hakurei.app/internal/dbus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() { gob.Register(new(spDBusOp)) }
|
func init() { gob.Register(new(spDBusOp)) }
|
||||||
|
|||||||
@ -6,12 +6,12 @@ import (
|
|||||||
|
|
||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
"hakurei.app/container/stub"
|
"hakurei.app/container/stub"
|
||||||
"hakurei.app/helper"
|
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
|
"hakurei.app/internal/acl"
|
||||||
|
"hakurei.app/internal/dbus"
|
||||||
|
"hakurei.app/internal/helper"
|
||||||
|
"hakurei.app/internal/system"
|
||||||
"hakurei.app/message"
|
"hakurei.app/message"
|
||||||
"hakurei.app/system"
|
|
||||||
"hakurei.app/system/acl"
|
|
||||||
"hakurei.app/system/dbus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSpDBusOp(t *testing.T) {
|
func TestSpDBusOp(t *testing.T) {
|
||||||
|
|||||||
@ -11,8 +11,8 @@ import (
|
|||||||
"hakurei.app/container/check"
|
"hakurei.app/container/check"
|
||||||
"hakurei.app/container/stub"
|
"hakurei.app/container/stub"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/system"
|
"hakurei.app/internal/acl"
|
||||||
"hakurei.app/system/acl"
|
"hakurei.app/internal/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSpPulseOp(t *testing.T) {
|
func TestSpPulseOp(t *testing.T) {
|
||||||
|
|||||||
@ -7,8 +7,8 @@ import (
|
|||||||
"hakurei.app/container/fhs"
|
"hakurei.app/container/fhs"
|
||||||
"hakurei.app/container/std"
|
"hakurei.app/container/std"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/system"
|
"hakurei.app/internal/acl"
|
||||||
"hakurei.app/system/acl"
|
"hakurei.app/internal/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@ -8,8 +8,8 @@ import (
|
|||||||
"hakurei.app/container/std"
|
"hakurei.app/container/std"
|
||||||
"hakurei.app/container/stub"
|
"hakurei.app/container/stub"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/system"
|
"hakurei.app/internal/acl"
|
||||||
"hakurei.app/system/acl"
|
"hakurei.app/internal/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSpRuntimeOp(t *testing.T) {
|
func TestSpRuntimeOp(t *testing.T) {
|
||||||
|
|||||||
@ -7,8 +7,8 @@ import (
|
|||||||
"hakurei.app/container/fhs"
|
"hakurei.app/container/fhs"
|
||||||
"hakurei.app/container/std"
|
"hakurei.app/container/std"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/system"
|
"hakurei.app/internal/acl"
|
||||||
"hakurei.app/system/acl"
|
"hakurei.app/internal/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() { gob.Register(spTmpdirOp{}) }
|
func init() { gob.Register(spTmpdirOp{}) }
|
||||||
|
|||||||
@ -8,8 +8,8 @@ import (
|
|||||||
"hakurei.app/container/std"
|
"hakurei.app/container/std"
|
||||||
"hakurei.app/container/stub"
|
"hakurei.app/container/stub"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/system"
|
"hakurei.app/internal/acl"
|
||||||
"hakurei.app/system/acl"
|
"hakurei.app/internal/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSpTmpdirOp(t *testing.T) {
|
func TestSpTmpdirOp(t *testing.T) {
|
||||||
|
|||||||
@ -5,8 +5,8 @@ import (
|
|||||||
|
|
||||||
"hakurei.app/container/check"
|
"hakurei.app/container/check"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/system/acl"
|
"hakurei.app/internal/acl"
|
||||||
"hakurei.app/system/wayland"
|
"hakurei.app/internal/wayland"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() { gob.Register(new(spWaylandOp)) }
|
func init() { gob.Register(new(spWaylandOp)) }
|
||||||
@ -25,8 +25,8 @@ func (s *spWaylandOp) toSystem(state *outcomeStateSys) error {
|
|||||||
|
|
||||||
// outer wayland socket (usually `/run/user/%d/wayland-%d`)
|
// outer wayland socket (usually `/run/user/%d/wayland-%d`)
|
||||||
var socketPath *check.Absolute
|
var socketPath *check.Absolute
|
||||||
if name, ok := state.k.lookupEnv(wayland.WaylandDisplay); !ok {
|
if name, ok := state.k.lookupEnv(wayland.Display); !ok {
|
||||||
state.msg.Verbose(wayland.WaylandDisplay + " is not set, assuming " + wayland.FallbackName)
|
state.msg.Verbose(wayland.Display + " is not set, assuming " + wayland.FallbackName)
|
||||||
socketPath = state.sc.RuntimePath.Append(wayland.FallbackName)
|
socketPath = state.sc.RuntimePath.Append(wayland.FallbackName)
|
||||||
} else if a, err := check.NewAbs(name); err != nil {
|
} else if a, err := check.NewAbs(name); err != nil {
|
||||||
socketPath = state.sc.RuntimePath.Append(name)
|
socketPath = state.sc.RuntimePath.Append(name)
|
||||||
@ -53,7 +53,7 @@ func (s *spWaylandOp) toSystem(state *outcomeStateSys) error {
|
|||||||
|
|
||||||
func (s *spWaylandOp) toContainer(state *outcomeStateParams) error {
|
func (s *spWaylandOp) toContainer(state *outcomeStateParams) error {
|
||||||
innerPath := state.runtimeDir.Append(wayland.FallbackName)
|
innerPath := state.runtimeDir.Append(wayland.FallbackName)
|
||||||
state.env[wayland.WaylandDisplay] = wayland.FallbackName
|
state.env[wayland.Display] = wayland.FallbackName
|
||||||
if s.SocketPath == nil {
|
if s.SocketPath == nil {
|
||||||
state.params.Bind(state.instancePath().Append("wayland"), innerPath, 0)
|
state.params.Bind(state.instancePath().Append("wayland"), innerPath, 0)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -6,9 +6,9 @@ import (
|
|||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
"hakurei.app/container/stub"
|
"hakurei.app/container/stub"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/system"
|
"hakurei.app/internal/acl"
|
||||||
"hakurei.app/system/acl"
|
"hakurei.app/internal/system"
|
||||||
"hakurei.app/system/wayland"
|
"hakurei.app/internal/wayland"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSpWaylandOp(t *testing.T) {
|
func TestSpWaylandOp(t *testing.T) {
|
||||||
@ -47,7 +47,7 @@ func TestSpWaylandOp(t *testing.T) {
|
|||||||
Ops: new(container.Ops).
|
Ops: new(container.Ops).
|
||||||
Bind(m(wantInstancePrefix+"/wayland"), m("/run/user/1000/wayland-0"), 0),
|
Bind(m(wantInstancePrefix+"/wayland"), m("/run/user/1000/wayland-0"), 0),
|
||||||
}, paramsWantEnv(config, map[string]string{
|
}, paramsWantEnv(config, map[string]string{
|
||||||
wayland.WaylandDisplay: wayland.FallbackName,
|
wayland.Display: wayland.FallbackName,
|
||||||
}, nil), nil},
|
}, nil), nil},
|
||||||
|
|
||||||
{"success direct", func(isShim, _ bool) outcomeOp {
|
{"success direct", func(isShim, _ bool) outcomeOp {
|
||||||
@ -75,7 +75,7 @@ func TestSpWaylandOp(t *testing.T) {
|
|||||||
Ops: new(container.Ops).
|
Ops: new(container.Ops).
|
||||||
Bind(m("/proc/nonexistent/wayland"), m("/run/user/1000/wayland-0"), 0),
|
Bind(m("/proc/nonexistent/wayland"), m("/run/user/1000/wayland-0"), 0),
|
||||||
}, paramsWantEnv(config, map[string]string{
|
}, paramsWantEnv(config, map[string]string{
|
||||||
wayland.WaylandDisplay: wayland.FallbackName,
|
wayland.Display: wayland.FallbackName,
|
||||||
}, nil), nil},
|
}, nil), nil},
|
||||||
|
|
||||||
{"success", func(bool, bool) outcomeOp {
|
{"success", func(bool, bool) outcomeOp {
|
||||||
@ -98,7 +98,7 @@ func TestSpWaylandOp(t *testing.T) {
|
|||||||
Ops: new(container.Ops).
|
Ops: new(container.Ops).
|
||||||
Bind(m(wantInstancePrefix+"/wayland"), m("/run/user/1000/wayland-0"), 0),
|
Bind(m(wantInstancePrefix+"/wayland"), m("/run/user/1000/wayland-0"), 0),
|
||||||
}, paramsWantEnv(config, map[string]string{
|
}, paramsWantEnv(config, map[string]string{
|
||||||
wayland.WaylandDisplay: wayland.FallbackName,
|
wayland.Display: wayland.FallbackName,
|
||||||
}, nil), nil},
|
}, nil), nil},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import (
|
|||||||
"hakurei.app/container/check"
|
"hakurei.app/container/check"
|
||||||
"hakurei.app/container/fhs"
|
"hakurei.app/container/fhs"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/system/acl"
|
"hakurei.app/internal/acl"
|
||||||
)
|
)
|
||||||
|
|
||||||
var absX11SocketDir = fhs.AbsTmp.Append(".X11-unix")
|
var absX11SocketDir = fhs.AbsTmp.Append(".X11-unix")
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import (
|
|||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
"hakurei.app/container/stub"
|
"hakurei.app/container/stub"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/system/acl"
|
"hakurei.app/internal/acl"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSpX11Op(t *testing.T) {
|
func TestSpX11Op(t *testing.T) {
|
||||||
|
|||||||
@ -27,7 +27,7 @@ func TestEntryData(t *testing.T) {
|
|||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
templateStateGob := mustEncodeGob(newTemplateState())
|
templateStateGob := mustEncodeGob(NewTemplateState())
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
@ -45,11 +45,11 @@ func TestEntryData(t *testing.T) {
|
|||||||
Step: "validate configuration", Err: hst.ErrConfigNull,
|
Step: "validate configuration", Err: hst.ErrConfigNull,
|
||||||
Msg: "invalid configuration"}},
|
Msg: "invalid configuration"}},
|
||||||
|
|
||||||
{"inconsistent enablement", "\x00\xff\xca\xfe\x00\x00\xff\x00" + templateStateGob, newTemplateState(), &hst.AppError{
|
{"inconsistent enablement", "\x00\xff\xca\xfe\x00\x00\xff\x00" + templateStateGob, NewTemplateState(), &hst.AppError{
|
||||||
Step: "validate state enablement", Err: os.ErrInvalid,
|
Step: "validate state enablement", Err: os.ErrInvalid,
|
||||||
Msg: "state entry aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa has unexpected enablement byte 0xd, 0xff"}},
|
Msg: "state entry aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa has unexpected enablement byte 0xd, 0xff"}},
|
||||||
|
|
||||||
{"template", "\x00\xff\xca\xfe\x00\x00\x0d\xf2" + templateStateGob, newTemplateState(), nil},
|
{"template", "\x00\xff\xca\xfe\x00\x00\x0d\xf2" + templateStateGob, NewTemplateState(), nil},
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
@ -105,7 +105,7 @@ func TestEntryData(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("encode fault", func(t *testing.T) {
|
t.Run("encode fault", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
s := newTemplateState()
|
s := NewTemplateState()
|
||||||
|
|
||||||
t.Run("gob", func(t *testing.T) {
|
t.Run("gob", func(t *testing.T) {
|
||||||
var want = &hst.AppError{Step: "encode state body", Err: stub.UniqueError(0xcafe)}
|
var want = &hst.AppError{Step: "encode state body", Err: stub.UniqueError(0xcafe)}
|
||||||
@ -123,8 +123,8 @@ func TestEntryData(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTemplateState returns the address of a new template [hst.State] struct.
|
// NewTemplateState returns the address of a new template [hst.State] struct.
|
||||||
func newTemplateState() *hst.State {
|
func NewTemplateState() *hst.State {
|
||||||
return &hst.State{
|
return &hst.State{
|
||||||
ID: hst.ID(bytes.Repeat([]byte{0xaa}, len(hst.ID{}))),
|
ID: hst.ID(bytes.Repeat([]byte{0xaa}, len(hst.ID{}))),
|
||||||
PID: 0xcafe,
|
PID: 0xcafe,
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
_ "unsafe"
|
_ "unsafe" // for go:linkname
|
||||||
|
|
||||||
"hakurei.app/container/check"
|
"hakurei.app/container/check"
|
||||||
"hakurei.app/container/stub"
|
"hakurei.app/container/stub"
|
||||||
@ -18,18 +18,23 @@ import (
|
|||||||
"hakurei.app/internal/store"
|
"hakurei.app/internal/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:linkname newTemplateState hakurei.app/internal/store.newTemplateState
|
// Made available here for direct validation of state entry files.
|
||||||
func newTemplateState() *hst.State
|
//
|
||||||
|
|
||||||
//go:linkname entryDecode hakurei.app/internal/store.entryDecode
|
//go:linkname entryDecode hakurei.app/internal/store.entryDecode
|
||||||
func entryDecode(r io.Reader, p *hst.State) (hst.Enablement, error)
|
func entryDecode(r io.Reader, p *hst.State) (hst.Enablement, error)
|
||||||
|
|
||||||
|
// Made available here for direct access to known segment handles.
|
||||||
|
//
|
||||||
//go:linkname newHandle hakurei.app/internal/store.newHandle
|
//go:linkname newHandle hakurei.app/internal/store.newHandle
|
||||||
func newHandle(base *check.Absolute, identity int) *store.Handle
|
func newHandle(base *check.Absolute, identity int) *store.Handle
|
||||||
|
|
||||||
|
// Made available here to check open error handling behaviour.
|
||||||
|
//
|
||||||
//go:linkname open hakurei.app/internal/store.(*EntryHandle).open
|
//go:linkname open hakurei.app/internal/store.(*EntryHandle).open
|
||||||
func open(eh *store.EntryHandle, flag int, perm os.FileMode) (*os.File, error)
|
func open(eh *store.EntryHandle, flag int, perm os.FileMode) (*os.File, error)
|
||||||
|
|
||||||
|
// Made available here to check the saveload cycle.
|
||||||
|
//
|
||||||
//go:linkname save hakurei.app/internal/store.(*EntryHandle).save
|
//go:linkname save hakurei.app/internal/store.(*EntryHandle).save
|
||||||
func save(eh *store.EntryHandle, state *hst.State) error
|
func save(eh *store.EntryHandle, state *hst.State) error
|
||||||
|
|
||||||
@ -91,9 +96,9 @@ func TestStateEntryHandle(t *testing.T) {
|
|||||||
t.Run("saveload", func(t *testing.T) {
|
t.Run("saveload", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
eh := store.EntryHandle{Pathname: check.MustAbs(t.TempDir()).Append("entry"),
|
eh := store.EntryHandle{Pathname: check.MustAbs(t.TempDir()).Append("entry"),
|
||||||
ID: newTemplateState().ID}
|
ID: store.NewTemplateState().ID}
|
||||||
|
|
||||||
if err := save(&eh, newTemplateState()); err != nil {
|
if err := save(&eh, store.NewTemplateState()); err != nil {
|
||||||
t.Fatalf("save: error = %v", err)
|
t.Fatalf("save: error = %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +117,7 @@ func TestStateEntryHandle(t *testing.T) {
|
|||||||
t.Fatal(f.Close())
|
t.Fatal(f.Close())
|
||||||
}
|
}
|
||||||
|
|
||||||
if want := newTemplateState(); !reflect.DeepEqual(&got, want) {
|
if want := store.NewTemplateState(); !reflect.DeepEqual(&got, want) {
|
||||||
t.Errorf("entryDecode: %#v, want %#v", &got, want)
|
t.Errorf("entryDecode: %#v, want %#v", &got, want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -122,7 +127,7 @@ func TestStateEntryHandle(t *testing.T) {
|
|||||||
|
|
||||||
if et, err := eh.Load(nil); err != nil {
|
if et, err := eh.Load(nil); err != nil {
|
||||||
t.Fatalf("load: error = %v", err)
|
t.Fatalf("load: error = %v", err)
|
||||||
} else if want := newTemplateState().Enablements.Unwrap(); et != want {
|
} else if want := store.NewTemplateState().Enablements.Unwrap(); et != want {
|
||||||
t.Errorf("load: et = %x, want %x", et, want)
|
t.Errorf("load: et = %x, want %x", et, want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -133,7 +138,7 @@ func TestStateEntryHandle(t *testing.T) {
|
|||||||
var got hst.State
|
var got hst.State
|
||||||
if _, err := eh.Load(&got); err != nil {
|
if _, err := eh.Load(&got); err != nil {
|
||||||
t.Fatalf("load: error = %v", err)
|
t.Fatalf("load: error = %v", err)
|
||||||
} else if want := newTemplateState(); !reflect.DeepEqual(&got, want) {
|
} else if want := store.NewTemplateState(); !reflect.DeepEqual(&got, want) {
|
||||||
t.Errorf("load: %#v, want %#v", &got, want)
|
t.Errorf("load: %#v, want %#v", &got, want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -12,13 +12,15 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
_ "unsafe"
|
_ "unsafe" // for go:linkname
|
||||||
|
|
||||||
"hakurei.app/container/check"
|
"hakurei.app/container/check"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/internal/store"
|
"hakurei.app/internal/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Made available here to check bigLock error handling behaviour.
|
||||||
|
//
|
||||||
//go:linkname bigLock hakurei.app/internal/store.(*Store).bigLock
|
//go:linkname bigLock hakurei.app/internal/store.(*Store).bigLock
|
||||||
func bigLock(s *store.Store) (unlock func(), err error)
|
func bigLock(s *store.Store) (unlock func(), err error)
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
"hakurei.app/container/check"
|
"hakurei.app/container/check"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/system/acl"
|
"hakurei.app/internal/acl"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UpdatePerm calls UpdatePermType with the [Process] criteria.
|
// UpdatePerm calls UpdatePermType with the [Process] criteria.
|
||||||
@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"hakurei.app/container/stub"
|
"hakurei.app/container/stub"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/system/acl"
|
"hakurei.app/internal/acl"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestACLUpdateOp(t *testing.T) {
|
func TestACLUpdateOp(t *testing.T) {
|
||||||
@ -13,7 +13,7 @@ import (
|
|||||||
|
|
||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/system/dbus"
|
"hakurei.app/internal/dbus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrDBusConfig is returned when a required [hst.BusConfig] argument is nil.
|
// ErrDBusConfig is returned when a required [hst.BusConfig] argument is nil.
|
||||||
@ -10,9 +10,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/container/stub"
|
"hakurei.app/container/stub"
|
||||||
"hakurei.app/helper"
|
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/system/dbus"
|
"hakurei.app/internal/dbus"
|
||||||
|
"hakurei.app/internal/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDBusProxyOp(t *testing.T) {
|
func TestDBusProxyOp(t *testing.T) {
|
||||||
@ -6,10 +6,12 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"hakurei.app/container/check"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/system/acl"
|
"hakurei.app/internal/acl"
|
||||||
"hakurei.app/system/dbus"
|
"hakurei.app/internal/dbus"
|
||||||
"hakurei.app/system/internal/xcb"
|
"hakurei.app/internal/wayland"
|
||||||
|
"hakurei.app/internal/xcb"
|
||||||
)
|
)
|
||||||
|
|
||||||
type osFile interface {
|
type osFile interface {
|
||||||
@ -45,6 +47,8 @@ type syscallDispatcher interface {
|
|||||||
// aclUpdate provides [acl.Update].
|
// aclUpdate provides [acl.Update].
|
||||||
aclUpdate(name string, uid int, perms ...acl.Perm) error
|
aclUpdate(name string, uid int, perms ...acl.Perm) error
|
||||||
|
|
||||||
|
waylandNew(displayPath, bindPath *check.Absolute, appID, instanceID string) (*wayland.SecurityContext, error)
|
||||||
|
|
||||||
// xcbChangeHosts provides [xcb.ChangeHosts].
|
// xcbChangeHosts provides [xcb.ChangeHosts].
|
||||||
xcbChangeHosts(mode xcb.HostMode, family xcb.Family, address string) error
|
xcbChangeHosts(mode xcb.HostMode, family xcb.Family, address string) error
|
||||||
|
|
||||||
@ -76,6 +80,10 @@ func (k direct) aclUpdate(name string, uid int, perms ...acl.Perm) error {
|
|||||||
return acl.Update(name, uid, perms...)
|
return acl.Update(name, uid, perms...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (k direct) waylandNew(displayPath, bindPath *check.Absolute, appID, instanceID string) (*wayland.SecurityContext, error) {
|
||||||
|
return wayland.New(displayPath, bindPath, appID, instanceID)
|
||||||
|
}
|
||||||
|
|
||||||
func (k direct) xcbChangeHosts(mode xcb.HostMode, family xcb.Family, address string) error {
|
func (k direct) xcbChangeHosts(mode xcb.HostMode, family xcb.Family, address string) error {
|
||||||
return xcb.ChangeHosts(mode, family, address)
|
return xcb.ChangeHosts(mode, family, address)
|
||||||
}
|
}
|
||||||
@ -8,11 +8,13 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"hakurei.app/container/check"
|
||||||
"hakurei.app/container/stub"
|
"hakurei.app/container/stub"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/system/acl"
|
"hakurei.app/internal/acl"
|
||||||
"hakurei.app/system/dbus"
|
"hakurei.app/internal/dbus"
|
||||||
"hakurei.app/system/internal/xcb"
|
"hakurei.app/internal/wayland"
|
||||||
|
"hakurei.app/internal/xcb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// call initialises a [stub.Call].
|
// call initialises a [stub.Call].
|
||||||
@ -268,6 +270,15 @@ func (k *kstub) aclUpdate(name string, uid int, perms ...acl.Perm) error {
|
|||||||
stub.CheckArgReflect(k.Stub, "perms", perms, 2))
|
stub.CheckArgReflect(k.Stub, "perms", perms, 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (k *kstub) waylandNew(displayPath, bindPath *check.Absolute, appID, instanceID string) (*wayland.SecurityContext, error) {
|
||||||
|
k.Helper()
|
||||||
|
return nil, k.Expects("waylandNew").Error(
|
||||||
|
stub.CheckArgReflect(k.Stub, "displayPath", displayPath, 0),
|
||||||
|
stub.CheckArgReflect(k.Stub, "bindPath", bindPath, 1),
|
||||||
|
stub.CheckArg(k.Stub, "appID", appID, 2),
|
||||||
|
stub.CheckArg(k.Stub, "instanceID", instanceID, 3))
|
||||||
|
}
|
||||||
|
|
||||||
func (k *kstub) xcbChangeHosts(mode xcb.HostMode, family xcb.Family, address string) error {
|
func (k *kstub) xcbChangeHosts(mode xcb.HostMode, family xcb.Family, address string) error {
|
||||||
k.Helper()
|
k.Helper()
|
||||||
return k.Expects("xcbChangeHosts").Error(
|
return k.Expects("xcbChangeHosts").Error(
|
||||||
@ -40,6 +40,10 @@ func (e *OpError) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *OpError) Message() string {
|
func (e *OpError) Message() string {
|
||||||
|
if m, ok := message.GetMessage(e.Err); ok {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case e.Msg != "":
|
case e.Msg != "":
|
||||||
return e.Error()
|
return e.Error()
|
||||||
@ -44,7 +44,7 @@ type Op interface {
|
|||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypeString extends [Enablement.String] to support [User] and [Process].
|
// TypeString extends [hst.Enablement.String] to support [User] and [Process].
|
||||||
func TypeString(e hst.Enablement) string {
|
func TypeString(e hst.Enablement) string {
|
||||||
switch e {
|
switch e {
|
||||||
case User:
|
case User:
|
||||||
@ -11,8 +11,8 @@ import (
|
|||||||
"hakurei.app/container/check"
|
"hakurei.app/container/check"
|
||||||
"hakurei.app/container/stub"
|
"hakurei.app/container/stub"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
|
"hakurei.app/internal/xcb"
|
||||||
"hakurei.app/message"
|
"hakurei.app/message"
|
||||||
"hakurei.app/system/internal/xcb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCriteria(t *testing.T) {
|
func TestCriteria(t *testing.T) {
|
||||||
81
internal/system/wayland.go
Normal file
81
internal/system/wayland.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"hakurei.app/container/check"
|
||||||
|
"hakurei.app/hst"
|
||||||
|
"hakurei.app/internal/acl"
|
||||||
|
"hakurei.app/internal/wayland"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Wayland maintains a wayland socket with security-context-v1 attached via [wayland].
|
||||||
|
// The socket stops accepting connections once the pipe referred to by sync is closed.
|
||||||
|
// The socket is pathname only and is destroyed on revert.
|
||||||
|
func (sys *I) Wayland(dst, src *check.Absolute, appID, instanceID string) *I {
|
||||||
|
sys.ops = append(sys.ops, &waylandOp{nil,
|
||||||
|
dst, src, appID, instanceID})
|
||||||
|
return sys
|
||||||
|
}
|
||||||
|
|
||||||
|
// waylandOp implements [I.Wayland].
|
||||||
|
type waylandOp struct {
|
||||||
|
ctx *wayland.SecurityContext
|
||||||
|
dst, src *check.Absolute
|
||||||
|
appID, instanceID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *waylandOp) Type() hst.Enablement { return Process }
|
||||||
|
|
||||||
|
func (w *waylandOp) apply(sys *I) (err error) {
|
||||||
|
if w.ctx, err = sys.waylandNew(w.src, w.dst, w.appID, w.instanceID); err != nil {
|
||||||
|
return newOpError("wayland", err, false)
|
||||||
|
} else {
|
||||||
|
sys.msg.Verbosef("wayland pathname socket on %q via %q", w.dst, w.src)
|
||||||
|
|
||||||
|
if err = sys.chmod(w.dst.String(), 0); err != nil {
|
||||||
|
if closeErr := w.ctx.Close(); closeErr != nil {
|
||||||
|
return newOpError("wayland", errors.Join(err, closeErr), false)
|
||||||
|
}
|
||||||
|
return newOpError("wayland", err, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = sys.aclUpdate(w.dst.String(), sys.uid, acl.Read, acl.Write, acl.Execute); err != nil {
|
||||||
|
if closeErr := w.ctx.Close(); closeErr != nil {
|
||||||
|
return newOpError("wayland", errors.Join(err, closeErr), false)
|
||||||
|
}
|
||||||
|
return newOpError("wayland", err, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *waylandOp) revert(sys *I, _ *Criteria) error {
|
||||||
|
var (
|
||||||
|
hangupErr error
|
||||||
|
removeErr error
|
||||||
|
)
|
||||||
|
|
||||||
|
sys.msg.Verbosef("hanging up wayland socket on %q", w.dst)
|
||||||
|
if w.ctx != nil {
|
||||||
|
hangupErr = w.ctx.Close()
|
||||||
|
}
|
||||||
|
if err := sys.remove(w.dst.String()); err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||||
|
removeErr = err
|
||||||
|
}
|
||||||
|
|
||||||
|
return newOpError("wayland", errors.Join(hangupErr, removeErr), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *waylandOp) Is(o Op) bool {
|
||||||
|
target, ok := o.(*waylandOp)
|
||||||
|
return ok && w != nil && target != nil &&
|
||||||
|
w.dst.Is(target.dst) && w.src.Is(target.src) &&
|
||||||
|
w.appID == target.appID && w.instanceID == target.instanceID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *waylandOp) Path() string { return w.dst.String() }
|
||||||
|
func (w *waylandOp) String() string { return fmt.Sprintf("wayland socket at %q", w.dst) }
|
||||||
157
internal/system/wayland_test.go
Normal file
157
internal/system/wayland_test.go
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"hakurei.app/container/stub"
|
||||||
|
"hakurei.app/internal/acl"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWaylandOp(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
checkOpBehaviour(t, []opBehaviourTestCase{
|
||||||
|
{"chmod", 0xbeef, 0xff, &waylandOp{nil,
|
||||||
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
|
||||||
|
m("/run/user/1971/wayland-0"),
|
||||||
|
"org.chromium.Chromium",
|
||||||
|
"ebf083d1b175911782d413369b64ce7c",
|
||||||
|
}, []stub.Call{
|
||||||
|
call("waylandNew", stub.ExpectArgs{m("/run/user/1971/wayland-0"), m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"), "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c"}, nil, nil),
|
||||||
|
call("verbosef", stub.ExpectArgs{"wayland pathname socket on %q via %q", []any{m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/1971/wayland-0")}}, nil, nil),
|
||||||
|
call("chmod", stub.ExpectArgs{"/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland", os.FileMode(0)}, nil, stub.UniqueError(3)),
|
||||||
|
}, &OpError{Op: "wayland", Err: errors.Join(stub.UniqueError(3), os.ErrInvalid)}, nil, nil},
|
||||||
|
|
||||||
|
{"aclUpdate", 0xbeef, 0xff, &waylandOp{nil,
|
||||||
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
|
||||||
|
m("/run/user/1971/wayland-0"),
|
||||||
|
"org.chromium.Chromium",
|
||||||
|
"ebf083d1b175911782d413369b64ce7c",
|
||||||
|
}, []stub.Call{
|
||||||
|
call("waylandNew", stub.ExpectArgs{m("/run/user/1971/wayland-0"), m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"), "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c"}, nil, nil),
|
||||||
|
call("verbosef", stub.ExpectArgs{"wayland pathname socket on %q via %q", []any{m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/1971/wayland-0")}}, nil, nil),
|
||||||
|
call("chmod", stub.ExpectArgs{"/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland", os.FileMode(0)}, nil, nil),
|
||||||
|
call("aclUpdate", stub.ExpectArgs{"/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland", 0xbeef, []acl.Perm{acl.Read, acl.Write, acl.Execute}}, nil, stub.UniqueError(2)),
|
||||||
|
}, &OpError{Op: "wayland", Err: errors.Join(stub.UniqueError(2), os.ErrInvalid)}, nil, nil},
|
||||||
|
|
||||||
|
{"remove", 0xbeef, 0xff, &waylandOp{nil,
|
||||||
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
|
||||||
|
m("/run/user/1971/wayland-0"),
|
||||||
|
"org.chromium.Chromium",
|
||||||
|
"ebf083d1b175911782d413369b64ce7c",
|
||||||
|
}, []stub.Call{
|
||||||
|
call("waylandNew", stub.ExpectArgs{m("/run/user/1971/wayland-0"), m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"), "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c"}, nil, nil),
|
||||||
|
call("verbosef", stub.ExpectArgs{"wayland pathname socket on %q via %q", []any{m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/1971/wayland-0")}}, nil, nil),
|
||||||
|
call("chmod", stub.ExpectArgs{"/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland", os.FileMode(0)}, nil, nil),
|
||||||
|
call("aclUpdate", stub.ExpectArgs{"/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland", 0xbeef, []acl.Perm{acl.Read, acl.Write, acl.Execute}}, nil, nil),
|
||||||
|
}, nil, []stub.Call{
|
||||||
|
call("verbosef", stub.ExpectArgs{"hanging up wayland socket on %q", []any{m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland")}}, nil, nil),
|
||||||
|
call("remove", stub.ExpectArgs{"/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"}, nil, stub.UniqueError(1)),
|
||||||
|
}, &OpError{Op: "wayland", Err: errors.Join(stub.UniqueError(1)), Revert: true}},
|
||||||
|
|
||||||
|
{"success", 0xbeef, 0xff, &waylandOp{nil,
|
||||||
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
|
||||||
|
m("/run/user/1971/wayland-0"),
|
||||||
|
"org.chromium.Chromium",
|
||||||
|
"ebf083d1b175911782d413369b64ce7c",
|
||||||
|
}, []stub.Call{
|
||||||
|
call("waylandNew", stub.ExpectArgs{m("/run/user/1971/wayland-0"), m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"), "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c"}, nil, nil),
|
||||||
|
call("verbosef", stub.ExpectArgs{"wayland pathname socket on %q via %q", []any{m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/1971/wayland-0")}}, nil, nil),
|
||||||
|
call("chmod", stub.ExpectArgs{"/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland", os.FileMode(0)}, nil, nil),
|
||||||
|
call("aclUpdate", stub.ExpectArgs{"/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland", 0xbeef, []acl.Perm{acl.Read, acl.Write, acl.Execute}}, nil, nil),
|
||||||
|
}, nil, []stub.Call{
|
||||||
|
call("verbosef", stub.ExpectArgs{"hanging up wayland socket on %q", []any{m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland")}}, nil, nil),
|
||||||
|
call("remove", stub.ExpectArgs{"/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"}, nil, nil),
|
||||||
|
}, nil},
|
||||||
|
})
|
||||||
|
|
||||||
|
checkOpsBuilder(t, "Wayland", []opsBuilderTestCase{
|
||||||
|
{"chromium", 0xcafe, func(_ *testing.T, sys *I) {
|
||||||
|
sys.Wayland(
|
||||||
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
|
||||||
|
m("/run/user/1971/wayland-0"),
|
||||||
|
"org.chromium.Chromium",
|
||||||
|
"ebf083d1b175911782d413369b64ce7c",
|
||||||
|
)
|
||||||
|
}, []Op{&waylandOp{nil,
|
||||||
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
|
||||||
|
m("/run/user/1971/wayland-0"),
|
||||||
|
"org.chromium.Chromium",
|
||||||
|
"ebf083d1b175911782d413369b64ce7c",
|
||||||
|
}}, stub.Expect{}},
|
||||||
|
})
|
||||||
|
|
||||||
|
checkOpIs(t, []opIsTestCase{
|
||||||
|
{"dst differs", &waylandOp{nil,
|
||||||
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7d/wayland"),
|
||||||
|
m("/run/user/1971/wayland-0"),
|
||||||
|
"org.chromium.Chromium",
|
||||||
|
"ebf083d1b175911782d413369b64ce7c",
|
||||||
|
}, &waylandOp{nil,
|
||||||
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
|
||||||
|
m("/run/user/1971/wayland-0"),
|
||||||
|
"org.chromium.Chromium",
|
||||||
|
"ebf083d1b175911782d413369b64ce7c",
|
||||||
|
}, false},
|
||||||
|
|
||||||
|
{"src differs", &waylandOp{nil,
|
||||||
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
|
||||||
|
m("/run/user/1971/wayland-1"),
|
||||||
|
"org.chromium.Chromium",
|
||||||
|
"ebf083d1b175911782d413369b64ce7c",
|
||||||
|
}, &waylandOp{nil,
|
||||||
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
|
||||||
|
m("/run/user/1971/wayland-0"),
|
||||||
|
"org.chromium.Chromium",
|
||||||
|
"ebf083d1b175911782d413369b64ce7c",
|
||||||
|
}, false},
|
||||||
|
|
||||||
|
{"appID differs", &waylandOp{nil,
|
||||||
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
|
||||||
|
m("/run/user/1971/wayland-0"),
|
||||||
|
"org.chromium",
|
||||||
|
"ebf083d1b175911782d413369b64ce7c",
|
||||||
|
}, &waylandOp{nil,
|
||||||
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
|
||||||
|
m("/run/user/1971/wayland-0"),
|
||||||
|
"org.chromium.Chromium",
|
||||||
|
"ebf083d1b175911782d413369b64ce7c",
|
||||||
|
}, false},
|
||||||
|
|
||||||
|
{"instanceID differs", &waylandOp{nil,
|
||||||
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
|
||||||
|
m("/run/user/1971/wayland-0"),
|
||||||
|
"org.chromium.Chromium",
|
||||||
|
"ebf083d1b175911782d413369b64ce7d",
|
||||||
|
}, &waylandOp{nil,
|
||||||
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
|
||||||
|
m("/run/user/1971/wayland-0"),
|
||||||
|
"org.chromium.Chromium",
|
||||||
|
"ebf083d1b175911782d413369b64ce7c",
|
||||||
|
}, false},
|
||||||
|
|
||||||
|
{"equals", &waylandOp{nil,
|
||||||
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
|
||||||
|
m("/run/user/1971/wayland-0"),
|
||||||
|
"org.chromium.Chromium",
|
||||||
|
"ebf083d1b175911782d413369b64ce7c",
|
||||||
|
}, &waylandOp{nil,
|
||||||
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
|
||||||
|
m("/run/user/1971/wayland-0"),
|
||||||
|
"org.chromium.Chromium",
|
||||||
|
"ebf083d1b175911782d413369b64ce7c",
|
||||||
|
}, true},
|
||||||
|
})
|
||||||
|
|
||||||
|
checkOpMeta(t, []opMetaTestCase{
|
||||||
|
{"chromium", &waylandOp{nil,
|
||||||
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
|
||||||
|
m("/run/user/1971/wayland-0"),
|
||||||
|
"org.chromium.Chromium",
|
||||||
|
"ebf083d1b175911782d413369b64ce7c",
|
||||||
|
}, Process, "/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland",
|
||||||
|
`wayland socket at "/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"`},
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -2,7 +2,7 @@ package system
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/system/internal/xcb"
|
"hakurei.app/internal/xcb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChangeHosts inserts the target user into X11 hosts and deletes it once its [Enablement] is no longer satisfied.
|
// ChangeHosts inserts the target user into X11 hosts and deletes it once its [Enablement] is no longer satisfied.
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user