From b1ea3b4acf85365fd76c890f8b8852b5f95be5c1 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Sat, 28 Mar 2026 16:43:02 +0900 Subject: [PATCH] cmd/hakurei: rename app to run The run command was a legacy holdover from very early days and is only useful for testing and demonstration these days. This change also renames it to exec. Signed-off-by: Ophestra --- cmd/hakurei/command.go | 10 +++++---- cmd/hakurei/command_test.go | 10 ++++----- cmd/hakurei/main.go | 12 +++------- dist/comp/_hakurei | 10 ++++----- nixos.nix | 2 +- test/sandbox/configuration.nix | 2 +- test/sandbox/test.py | 18 +++++++-------- test/test.py | 40 +++++++++++++++++----------------- 8 files changed, 50 insertions(+), 54 deletions(-) diff --git a/cmd/hakurei/command.go b/cmd/hakurei/command.go index b40b6b86..6e5b446a 100644 --- a/cmd/hakurei/command.go +++ b/cmd/hakurei/command.go @@ -34,6 +34,8 @@ func optionalErrorUnwrap(err error) error { return err } +var errSuccess = errors.New("success") + func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErrs, out io.Writer) command.Command { var ( flagVerbose bool @@ -63,9 +65,9 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr var ( flagIdentifierFile int ) - c.NewCommand("app", "Load and start container from configuration file", func(args []string) error { + c.NewCommand("run", "Load and start container from configuration file", func(args []string) error { if len(args) < 1 { - log.Fatal("app requires at least 1 argument") + log.Fatal("run requires at least 1 argument") } config := tryPath(msg, args[0]) @@ -101,7 +103,7 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr flagWayland, flagX11, flagDBus, flagPipeWire, flagPulse bool ) - c.NewCommand("run", "Configure and start a permissive container", func(args []string) error { + c.NewCommand("exec", "Configure and start a permissive container", func(args []string) error { if flagIdentity < hst.IdentityStart || flagIdentity > hst.IdentityEnd { log.Fatalf("identity %d out of range", flagIdentity) } @@ -326,7 +328,7 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr flagShort bool flagNoStore bool ) - c.NewCommand("show", "Show live or local app configuration", func(args []string) error { + c.NewCommand("show", "Show live or local instance configuration", func(args []string) error { switch len(args) { case 0: // system printShowSystem(os.Stdout, flagShort, flagJSON) diff --git a/cmd/hakurei/command_test.go b/cmd/hakurei/command_test.go index 67c80697..a7a5027e 100644 --- a/cmd/hakurei/command_test.go +++ b/cmd/hakurei/command_test.go @@ -23,9 +23,9 @@ func TestHelp(t *testing.T) { Usage: hakurei [-h | --help] [-v] [--json] COMMAND [OPTIONS] Commands: - app Load and start container from configuration file - run Configure and start a permissive container - show Show live or local app configuration + run Load and start container from configuration file + exec Configure and start a permissive container + show Show live or local instance configuration ps List active instances version Display version information license Show full license text @@ -35,8 +35,8 @@ Commands: `, }, { - "run", []string{"run", "-h"}, ` -Usage: hakurei run [-h | --help] [--dbus-config ] [--dbus-system ] [--mpris] [--dbus-log] [--id ] [-a ] [-g ] [-d ] [-u ] [--policy ] [--priority ] [--private-runtime] [--private-tmpdir] [--wayland] [-X] [--dbus] [--pipewire] [--pulse] COMMAND [OPTIONS] + "exec", []string{"exec", "-h"}, ` +Usage: hakurei exec [-h | --help] [--dbus-config ] [--dbus-system ] [--mpris] [--dbus-log] [--id ] [-a ] [-g ] [-d ] [-u ] [--policy ] [--priority ] [--private-runtime] [--private-tmpdir] [--wayland] [-X] [--dbus] [--pipewire] [--pulse] COMMAND [OPTIONS] Flags: -X Enable direct connection to X11 diff --git a/cmd/hakurei/main.go b/cmd/hakurei/main.go index b7722bc3..a5b14d9c 100644 --- a/cmd/hakurei/main.go +++ b/cmd/hakurei/main.go @@ -1,8 +1,5 @@ package main -// this works around go:embed '..' limitation -//go:generate cp ../../LICENSE . - import ( "context" _ "embed" @@ -17,12 +14,9 @@ import ( "hakurei.app/message" ) -var ( - errSuccess = errors.New("success") - - //go:embed LICENSE - license string -) +//go:generate cp ../../LICENSE . +//go:embed LICENSE +var license string // earlyHardeningErrs are errors collected while setting up early hardening feature. type earlyHardeningErrs struct{ yamaLSM, dumpable error } diff --git a/dist/comp/_hakurei b/dist/comp/_hakurei index 8230ba4c..11424612 100644 --- a/dist/comp/_hakurei +++ b/dist/comp/_hakurei @@ -1,11 +1,11 @@ #compdef hakurei -_hakurei_app() { +_hakurei_run() { __hakurei_files return $? } -_hakurei_run() { +_hakurei_exec() { _arguments \ '--id[Reverse-DNS style Application identifier, leave empty to inherit instance identifier]:id' \ '-a[Application identity]: :_numbers' \ @@ -57,9 +57,9 @@ __hakurei_instances() { { local -a _hakurei_cmds _hakurei_cmds=( - "app:Load and start container from configuration file" - "run:Configure and start a permissive container" - "show:Show live or local app configuration" + "run:Load and start container from configuration file" + "exec:Configure and start a permissive container" + "show:Show live or local instance configuration" "ps:List active instances" "version:Display version information" "license:Show full license text" diff --git a/nixos.nix b/nixos.nix index 15ec5287..5b60aea4 100644 --- a/nixos.nix +++ b/nixos.nix @@ -265,7 +265,7 @@ in ''; in pkgs.writeShellScriptBin app.name '' - exec hakurei${if app.verbose then " -v" else ""} app ${checkedConfig "hakurei-app-${app.name}.json" conf} $@ + exec hakurei${if app.verbose then " -v" else ""} run ${checkedConfig "hakurei-app-${app.name}.json" conf} $@ '' ) ] diff --git a/test/sandbox/configuration.nix b/test/sandbox/configuration.nix index 80c92fdf..bc189f7d 100644 --- a/test/sandbox/configuration.nix +++ b/test/sandbox/configuration.nix @@ -30,7 +30,7 @@ in # For checking pd outcome: (pkgs.writeShellScriptBin "check-sandbox-pd" '' - hakurei -v run hakurei-test \ + hakurei -v exec hakurei-test \ -p "/var/tmp/.hakurei-check-ok.0" \ -t ${toString (builtins.toFile "hakurei-pd-want.json" (builtins.toJSON testCases.pd.want))} \ -s ${testCases.pd.expectedFilter.${pkgs.stdenv.hostPlatform.system}} "$@" diff --git a/test/sandbox/test.py b/test/sandbox/test.py index c1191483..f188a01d 100644 --- a/test/sandbox/test.py +++ b/test/sandbox/test.py @@ -42,23 +42,23 @@ machine.wait_for_file("/run/user/1000/wayland-1") machine.wait_for_file("/tmp/sway-ipc.sock") # Check pd seccomp outcome: -swaymsg("exec hakurei run cat") +swaymsg("exec hakurei exec cat") check_filter(0, "pdlike", "cat") # Check fd leak: -swaymsg("exec exec 127/dev/stdout") +denyOutput = machine.fail("sudo -u untrusted -i hakurei exec &>/dev/stdout") print(denyOutput) -denyOutputVerbose = machine.fail("sudo -u untrusted -i hakurei -v run &>/dev/stdout") +denyOutputVerbose = machine.fail("sudo -u untrusted -i hakurei -v exec &>/dev/stdout") print(denyOutputVerbose) # Fail direct hsu call: @@ -118,11 +118,11 @@ def hakurei_identity(offset): # Start hakurei permissive defaults outside Wayland session: -print(machine.succeed("sudo -u alice -i hakurei -v run -a 0 touch /tmp/pd-bare-ok")) +print(machine.succeed("sudo -u alice -i hakurei -v exec -a 0 touch /tmp/pd-bare-ok")) machine.wait_for_file("/tmp/hakurei.0/tmpdir/0/pd-bare-ok", timeout=5) # Verify silent output permissive defaults: -output = machine.succeed("sudo -u alice -i hakurei run -a 0 true &>/dev/stdout") +output = machine.succeed("sudo -u alice -i hakurei exec -a 0 true &>/dev/stdout") if output != "": raise Exception(f"unexpected output\n{output}") @@ -131,12 +131,12 @@ def silent_output_interrupt(flags): swaymsg("exec foot") wait_for_window("alice@machine") # identity 0 does not have home-manager - machine.send_chars(f"exec hakurei run {flags}-a 0 sh -c 'export PATH=/run/current-system/sw/bin:$PATH && touch /tmp/pd-silent-ready && sleep infinity' &>/tmp/pd-silent\n") + machine.send_chars(f"exec hakurei exec {flags}-a 0 sh -c 'export PATH=/run/current-system/sw/bin:$PATH && touch /tmp/pd-silent-ready && sleep infinity' &>/tmp/pd-silent\n") machine.wait_for_file("/tmp/hakurei.0/tmpdir/0/pd-silent-ready", timeout=15) machine.succeed("rm /tmp/hakurei.0/tmpdir/0/pd-silent-ready") machine.send_key("ctrl-c") machine.wait_until_fails("pgrep foot", timeout=5) - machine.wait_until_fails(f"pgrep -u alice -f 'hakurei run {flags}-a 0 '", timeout=5) + machine.wait_until_fails(f"pgrep -u alice -f 'hakurei exec {flags}-a 0 '", timeout=5) output = machine.succeed("cat /tmp/pd-silent && rm /tmp/pd-silent") if output != "": raise Exception(f"unexpected output\n{output}") @@ -147,10 +147,10 @@ silent_output_interrupt("--dbus ") # this one is especially painful as it mainta silent_output_interrupt("--wayland -X --dbus --pulse ") # Verify graceful failure on bad Wayland display name: -print(machine.fail("sudo -u alice -i hakurei -v run --wayland true")) +print(machine.fail("sudo -u alice -i hakurei -v exec --wayland true")) # Start hakurei permissive defaults within Wayland session: -hakurei('-v run --wayland --dbus --dbus-log notify-send -a "NixOS Tests" "Test notification" "Notification from within sandbox." && touch /tmp/dbus-ok') +hakurei('-v exec --wayland --dbus --dbus-log notify-send -a "NixOS Tests" "Test notification" "Notification from within sandbox." && touch /tmp/dbus-ok') machine.wait_for_file("/tmp/dbus-ok", timeout=15) collect_state_ui("dbus_notify_exited") # not in pid namespace, verify termination @@ -158,10 +158,10 @@ machine.wait_until_fails("pgrep xdg-dbus-proxy") machine.succeed("pkill -9 mako") # Check revert type selection: -hakurei("-v run --wayland -X --dbus --pulse -u p0 foot && touch /tmp/p0-exit-ok") +hakurei("-v exec --wayland -X --dbus --pulse -u p0 foot && touch /tmp/p0-exit-ok") wait_for_window("p0@machine") print(machine.succeed("getfacl --absolute-names --omit-header --numeric /tmp/hakurei.0/runtime | grep 10000")) -hakurei("-v run --wayland -X --dbus --pulse -u p1 foot && touch /tmp/p1-exit-ok") +hakurei("-v exec --wayland -X --dbus --pulse -u p1 foot && touch /tmp/p1-exit-ok") wait_for_window("p1@machine") print(machine.succeed("getfacl --absolute-names --omit-header --numeric /tmp/hakurei.0/runtime | grep 10000")) machine.send_chars("exit\n") @@ -173,14 +173,14 @@ machine.wait_for_file("/tmp/p0-exit-ok", timeout=15) machine.fail("getfacl --absolute-names --omit-header --numeric /tmp/hakurei.0/runtime | grep 10000") # Check invalid identifier fd behaviour: -machine.fail('echo \'{"container":{"shell":"/proc/nonexistent","home":"/proc/nonexistent","path":"/proc/nonexistent"}}\' | sudo -u alice -i hakurei -v app --identifier-fd 32767 - 2>&1 | tee > /tmp/invalid-identifier-fd') +machine.fail('echo \'{"container":{"shell":"/proc/nonexistent","home":"/proc/nonexistent","path":"/proc/nonexistent"}}\' | sudo -u alice -i hakurei -v run --identifier-fd 32767 - 2>&1 | tee > /tmp/invalid-identifier-fd') machine.wait_for_file("/tmp/invalid-identifier-fd") print(machine.succeed('grep "^hakurei: cannot write identifier: bad file descriptor$" /tmp/invalid-identifier-fd')) # Check interrupt shim behaviour: swaymsg("exec sh -c 'ne-foot; echo -n $? > /tmp/monitor-exit-code'") wait_for_window(f"u0_a{hakurei_identity(0)}@machine") -machine.succeed("pkill -INT -f 'hakurei -v app '") +machine.succeed("pkill -INT -f 'hakurei -v run '") machine.wait_until_fails("pgrep foot", timeout=5) machine.wait_for_file("/tmp/monitor-exit-code") interrupt_exit_code = int(machine.succeed("cat /tmp/monitor-exit-code")) @@ -190,7 +190,7 @@ if interrupt_exit_code != 230: # Check interrupt shim behaviour immediate termination: swaymsg("exec sh -c 'ne-foot-immediate; echo -n $? > /tmp/monitor-exit-code'") wait_for_window(f"u0_a{hakurei_identity(0)}@machine") -machine.succeed("pkill -INT -f 'hakurei -v app '") +machine.succeed("pkill -INT -f 'hakurei -v run '") machine.wait_until_fails("pgrep foot", timeout=5) machine.wait_for_file("/tmp/monitor-exit-code") interrupt_exit_code = int(machine.succeed("cat /tmp/monitor-exit-code")) @@ -201,19 +201,19 @@ if interrupt_exit_code != 254: swaymsg("exec sh -c 'ne-foot &> /tmp/shim-cont-unexpected-pid'") wait_for_window(f"u0_a{hakurei_identity(0)}@machine") machine.succeed("pkill -CONT -f 'hakurei shim'") -machine.succeed("pkill -INT -f 'hakurei -v app '") +machine.succeed("pkill -INT -f 'hakurei -v run '") machine.wait_until_fails("pgrep foot", timeout=5) machine.wait_for_file("/tmp/shim-cont-unexpected-pid") print(machine.succeed('grep "shim: got SIGCONT from unexpected process$" /tmp/shim-cont-unexpected-pid')) # Check setscheduler: -sched_unset = int(machine.succeed("sudo -u alice -i hakurei -v run cat /proc/self/sched | grep '^policy' | tr -d ' ' | cut -d ':' -f 2")) +sched_unset = int(machine.succeed("sudo -u alice -i hakurei -v exec cat /proc/self/sched | grep '^policy' | tr -d ' ' | cut -d ':' -f 2")) if sched_unset != 0: raise Exception(f"unexpected unset policy: {sched_unset}") -sched_idle = int(machine.succeed("sudo -u alice -i hakurei -v run --policy=idle cat /proc/self/sched | grep '^policy' | tr -d ' ' | cut -d ':' -f 2")) +sched_idle = int(machine.succeed("sudo -u alice -i hakurei -v exec --policy=idle cat /proc/self/sched | grep '^policy' | tr -d ' ' | cut -d ':' -f 2")) if sched_idle != 5: raise Exception(f"unexpected idle policy: {sched_idle}") -sched_rr = int(machine.succeed("sudo -u alice -i hakurei -v run --policy=rr cat /proc/self/sched | grep '^policy' | tr -d ' ' | cut -d ':' -f 2")) +sched_rr = int(machine.succeed("sudo -u alice -i hakurei -v exec --policy=rr cat /proc/self/sched | grep '^policy' | tr -d ' ' | cut -d ':' -f 2")) if sched_rr != 2: raise Exception(f"unexpected round-robin policy: {sched_idle}") @@ -243,11 +243,11 @@ machine.wait_until_fails("pgrep foot", timeout=5) machine.wait_until_fails("pgrep -x hakurei", timeout=5) machine.succeed("find /tmp -maxdepth 1 -type d -name '.hakurei-shim-*' -print -exec false '{}' +") # Test PipeWire SecurityContext: -machine.succeed("sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 hakurei -v run --pulse pactl info") -machine.fail("sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 hakurei -v run --pulse pactl set-sink-mute @DEFAULT_SINK@ toggle") +machine.succeed("sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 hakurei -v exec --pulse pactl info") +machine.fail("sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 hakurei -v exec --pulse pactl set-sink-mute @DEFAULT_SINK@ toggle") # Test PipeWire direct access: machine.succeed("sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 pw-dump") -machine.fail("sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 hakurei -v run --pipewire pw-dump") +machine.fail("sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 hakurei -v exec --pipewire pw-dump") # Test XWayland (foot does not support X): swaymsg("exec x11-alacritty")