aboutsummaryrefslogtreecommitdiffhomepage
path: root/cmd
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-09-29 02:33:10 +0900
committerOphestra <cat@gensokyo.uk>2025-09-29 06:11:47 +0900
commit46cd3a28c83e93b5d66c52d06a8366c11910d5c0 (patch)
tree6e25c5078b5cd9de78b7a6c9b253f1132358812b /cmd
parentad1bc6794f0053c3e63eab408a0d530d53e8b51c (diff)
container: remove global msg
This frees all container instances of side effects. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'cmd')
-rw-r--r--cmd/hakurei/command.go39
-rw-r--r--cmd/hakurei/command_test.go3
-rw-r--r--cmd/hakurei/main.go26
-rw-r--r--cmd/hakurei/parse.go24
-rw-r--r--cmd/hakurei/print.go5
-rw-r--r--cmd/hpkg/main.go37
-rw-r--r--cmd/hpkg/paths.go5
-rw-r--r--cmd/hpkg/proc.go13
-rw-r--r--cmd/hpkg/with.go17
9 files changed, 92 insertions, 77 deletions
diff --git a/cmd/hakurei/command.go b/cmd/hakurei/command.go
index 1671902e..58e8ace1 100644
--- a/cmd/hakurei/command.go
+++ b/cmd/hakurei/command.go
@@ -17,17 +17,30 @@ import (
"hakurei.app/internal"
"hakurei.app/internal/app"
"hakurei.app/internal/app/state"
- "hakurei.app/internal/hlog"
"hakurei.app/system"
"hakurei.app/system/dbus"
)
-func buildCommand(ctx context.Context, out io.Writer) command.Command {
+func buildCommand(ctx context.Context, msg container.Msg, early *earlyHardeningErrs, out io.Writer) command.Command {
var (
flagVerbose bool
flagJSON bool
)
- c := command.New(out, log.Printf, "hakurei", func([]string) error { internal.InstallOutput(flagVerbose); return nil }).
+ c := command.New(out, log.Printf, "hakurei", func([]string) error {
+ msg.SwapVerbose(flagVerbose)
+
+ if early.yamaLSM != nil {
+ msg.Verbosef("cannot enable ptrace protection via Yama LSM: %v", early.yamaLSM)
+ // not fatal
+ }
+
+ if early.dumpable != nil {
+ log.Printf("cannot set SUID_DUMP_DISABLE: %s", early.dumpable)
+ // not fatal
+ }
+
+ return nil
+ }).
Flag(&flagVerbose, "v", command.BoolFlag(false), "Increase log verbosity").
Flag(&flagJSON, "json", command.BoolFlag(false), "Serialise output in JSON when applicable")
@@ -39,10 +52,10 @@ func buildCommand(ctx context.Context, out io.Writer) command.Command {
}
// config extraArgs...
- config := tryPath(args[0])
+ config := tryPath(msg, args[0])
config.Args = append(config.Args, args[1:]...)
- app.Main(ctx, config)
+ app.Main(ctx, msg, config)
panic("unreachable")
})
@@ -78,9 +91,9 @@ func buildCommand(ctx context.Context, out io.Writer) command.Command {
passwd *user.User
passwdOnce sync.Once
passwdFunc = func() {
- us := strconv.Itoa(app.HsuUid(new(app.Hsu).MustID(), flagIdentity))
+ us := strconv.Itoa(app.HsuUid(new(app.Hsu).MustIDMsg(msg), flagIdentity))
if u, err := user.LookupId(us); err != nil {
- hlog.Verbosef("cannot look up uid %s", us)
+ msg.Verbosef("cannot look up uid %s", us)
passwd = &user.User{
Uid: us,
Gid: us,
@@ -162,7 +175,7 @@ func buildCommand(ctx context.Context, out io.Writer) command.Command {
}
}
- app.Main(ctx, config)
+ app.Main(ctx, msg, config)
panic("unreachable")
}).
Flag(&flagDBusConfigSession, "dbus-config", command.StringFlag("builtin"),
@@ -198,13 +211,13 @@ func buildCommand(ctx context.Context, out io.Writer) command.Command {
c.NewCommand("show", "Show live or local app configuration", func(args []string) error {
switch len(args) {
case 0: // system
- printShowSystem(os.Stdout, flagShort, flagJSON)
+ printShowSystem(msg, os.Stdout, flagShort, flagJSON)
case 1: // instance
name := args[0]
- config, entry := tryShort(name)
+ config, entry := tryShort(msg, name)
if config == nil {
- config = tryPath(name)
+ config = tryPath(msg, name)
}
printShowInstance(os.Stdout, time.Now().UTC(), entry, config, flagShort, flagJSON)
@@ -219,8 +232,8 @@ func buildCommand(ctx context.Context, out io.Writer) command.Command {
var flagShort bool
c.NewCommand("ps", "List active instances", func(args []string) error {
var sc hst.Paths
- app.CopyPaths(&sc, new(app.Hsu).MustID())
- printPs(os.Stdout, time.Now().UTC(), state.NewMulti(sc.RunDirPath.String()), flagShort, flagJSON)
+ app.CopyPaths(msg, &sc, new(app.Hsu).MustID())
+ printPs(os.Stdout, time.Now().UTC(), state.NewMulti(msg, sc.RunDirPath.String()), flagShort, flagJSON)
return errSuccess
}).Flag(&flagShort, "short", command.BoolFlag(false), "Print instance id")
}
diff --git a/cmd/hakurei/command_test.go b/cmd/hakurei/command_test.go
index 8ce9a23d..dd16b250 100644
--- a/cmd/hakurei/command_test.go
+++ b/cmd/hakurei/command_test.go
@@ -7,6 +7,7 @@ import (
"testing"
"hakurei.app/command"
+ "hakurei.app/container"
)
func TestHelp(t *testing.T) {
@@ -68,7 +69,7 @@ Flags:
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
out := new(bytes.Buffer)
- c := buildCommand(t.Context(), out)
+ c := buildCommand(t.Context(), container.NewMsg(nil), new(earlyHardeningErrs), out)
if err := c.Parse(tc.args); !errors.Is(err, command.ErrHelp) && !errors.Is(err, flag.ErrHelp) {
t.Errorf("Parse: error = %v; want %v",
err, command.ErrHelp)
diff --git a/cmd/hakurei/main.go b/cmd/hakurei/main.go
index 2b9193b6..5cc8224d 100644
--- a/cmd/hakurei/main.go
+++ b/cmd/hakurei/main.go
@@ -13,8 +13,6 @@ import (
"syscall"
"hakurei.app/container"
- "hakurei.app/internal"
- "hakurei.app/internal/hlog"
)
var (
@@ -24,20 +22,20 @@ var (
license string
)
-func init() { hlog.Prepare("hakurei") }
+// earlyHardeningErrs are errors collected while setting up early hardening feature.
+type earlyHardeningErrs struct{ yamaLSM, dumpable error }
func main() {
// early init path, skips root check and duplicate PR_SET_DUMPABLE
- container.TryArgv0(hlog.Output{}, hlog.Prepare, internal.InstallOutput)
+ container.TryArgv0(nil)
- if err := container.SetPtracer(0); err != nil {
- hlog.Verbosef("cannot enable ptrace protection via Yama LSM: %v", err)
- // not fatal: this program runs as the privileged user
- }
+ log.SetPrefix("hakurei: ")
+ log.SetFlags(0)
+ msg := container.NewMsg(log.Default())
- if err := container.SetDumpable(container.SUID_DUMP_DISABLE); err != nil {
- log.Printf("cannot set SUID_DUMP_DISABLE: %s", err)
- // not fatal: this program runs as the privileged user
+ early := earlyHardeningErrs{
+ yamaLSM: container.SetPtracer(0),
+ dumpable: container.SetDumpable(container.SUID_DUMP_DISABLE),
}
if os.Geteuid() == 0 {
@@ -48,10 +46,10 @@ func main() {
syscall.SIGINT, syscall.SIGTERM)
defer stop() // unreachable
- buildCommand(ctx, os.Stderr).MustParse(os.Args[1:], func(err error) {
- hlog.Verbosef("command returned %v", err)
+ buildCommand(ctx, msg, &early, os.Stderr).MustParse(os.Args[1:], func(err error) {
+ msg.Verbosef("command returned %v", err)
if errors.Is(err, errSuccess) {
- hlog.BeforeExit()
+ msg.BeforeExit()
os.Exit(0)
}
// this catches faulty command handlers that fail to return before this point
diff --git a/cmd/hakurei/parse.go b/cmd/hakurei/parse.go
index 897382ea..3825ca1f 100644
--- a/cmd/hakurei/parse.go
+++ b/cmd/hakurei/parse.go
@@ -10,20 +10,20 @@ import (
"strings"
"syscall"
+ "hakurei.app/container"
"hakurei.app/hst"
"hakurei.app/internal/app"
"hakurei.app/internal/app/state"
- "hakurei.app/internal/hlog"
)
-func tryPath(name string) (config *hst.Config) {
+func tryPath(msg container.Msg, name string) (config *hst.Config) {
var r io.Reader
config = new(hst.Config)
if name != "-" {
- r = tryFd(name)
+ r = tryFd(msg, name)
if r == nil {
- hlog.Verbose("load configuration from file")
+ msg.Verbose("load configuration from file")
if f, err := os.Open(name); err != nil {
log.Fatalf("cannot access configuration file %q: %s", name, err)
@@ -49,14 +49,14 @@ func tryPath(name string) (config *hst.Config) {
return
}
-func tryFd(name string) io.ReadCloser {
+func tryFd(msg container.Msg, name string) io.ReadCloser {
if v, err := strconv.Atoi(name); err != nil {
if !errors.Is(err, strconv.ErrSyntax) {
- hlog.Verbosef("name cannot be interpreted as int64: %v", err)
+ msg.Verbosef("name cannot be interpreted as int64: %v", err)
}
return nil
} else {
- hlog.Verbosef("trying config stream from %d", v)
+ msg.Verbosef("trying config stream from %d", v)
fd := uintptr(v)
if _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, fd, syscall.F_GETFD, 0); errno != 0 {
if errors.Is(errno, syscall.EBADF) {
@@ -68,7 +68,7 @@ func tryFd(name string) io.ReadCloser {
}
}
-func tryShort(name string) (config *hst.Config, entry *state.State) {
+func tryShort(msg container.Msg, name string) (config *hst.Config, entry *state.State) {
likePrefix := false
if len(name) <= 32 {
likePrefix = true
@@ -86,11 +86,11 @@ func tryShort(name string) (config *hst.Config, entry *state.State) {
// try to match from state store
if likePrefix && len(name) >= 8 {
- hlog.Verbose("argument looks like prefix")
+ msg.Verbose("argument looks like prefix")
var sc hst.Paths
- app.CopyPaths(&sc, new(app.Hsu).MustID())
- s := state.NewMulti(sc.RunDirPath.String())
+ app.CopyPaths(msg, &sc, new(app.Hsu).MustID())
+ s := state.NewMulti(msg, sc.RunDirPath.String())
if entries, err := state.Join(s); err != nil {
log.Printf("cannot join store: %v", err)
// drop to fetch from file
@@ -104,7 +104,7 @@ func tryShort(name string) (config *hst.Config, entry *state.State) {
break
}
- hlog.Verbosef("instance %s skipped", v)
+ msg.Verbosef("instance %s skipped", v)
}
}
}
diff --git a/cmd/hakurei/print.go b/cmd/hakurei/print.go
index 938517b8..2c575386 100644
--- a/cmd/hakurei/print.go
+++ b/cmd/hakurei/print.go
@@ -11,18 +11,19 @@ import (
"text/tabwriter"
"time"
+ "hakurei.app/container"
"hakurei.app/hst"
"hakurei.app/internal/app"
"hakurei.app/internal/app/state"
"hakurei.app/system/dbus"
)
-func printShowSystem(output io.Writer, short, flagJSON bool) {
+func printShowSystem(msg container.Msg, output io.Writer, short, flagJSON bool) {
t := newPrinter(output)
defer t.MustFlush()
info := &hst.Info{User: new(app.Hsu).MustID()}
- app.CopyPaths(&info.Paths, info.User)
+ app.CopyPaths(msg, &info.Paths, info.User)
if flagJSON {
printJSON(output, short, info)
diff --git a/cmd/hpkg/main.go b/cmd/hpkg/main.go
index 2ae646c3..03247aac 100644
--- a/cmd/hpkg/main.go
+++ b/cmd/hpkg/main.go
@@ -13,22 +13,21 @@ import (
"hakurei.app/command"
"hakurei.app/container"
"hakurei.app/hst"
- "hakurei.app/internal"
- "hakurei.app/internal/hlog"
)
var (
errSuccess = errors.New("success")
)
-func init() {
- hlog.Prepare("hpkg")
+func main() {
+ log.SetPrefix("hpkg: ")
+ log.SetFlags(0)
+ msg := container.NewMsg(log.Default())
+
if err := os.Setenv("SHELL", pathShell.String()); err != nil {
log.Fatalf("cannot set $SHELL: %v", err)
}
-}
-func main() {
if os.Geteuid() == 0 {
log.Fatal("this program must not run as root")
}
@@ -41,7 +40,7 @@ func main() {
flagVerbose bool
flagDropShell bool
)
- c := command.New(os.Stderr, log.Printf, "hpkg", func([]string) error { internal.InstallOutput(flagVerbose); return nil }).
+ c := command.New(os.Stderr, log.Printf, "hpkg", func([]string) error { msg.SwapVerbose(flagVerbose); return nil }).
Flag(&flagVerbose, "v", command.BoolFlag(false), "Print debug messages to the console").
Flag(&flagDropShell, "s", command.BoolFlag(false), "Drop to a shell in place of next hakurei action")
@@ -90,12 +89,12 @@ func main() {
}
cleanup := func() {
// should be faster than a native implementation
- mustRun(chmod, "-R", "+w", workDir.String())
- mustRun(rm, "-rf", workDir.String())
+ mustRun(msg, chmod, "-R", "+w", workDir.String())
+ mustRun(msg, rm, "-rf", workDir.String())
}
beforeRunFail.Store(&cleanup)
- mustRun(tar, "-C", workDir.String(), "-xf", pkgPath)
+ mustRun(msg, tar, "-C", workDir.String(), "-xf", pkgPath)
/*
Parse bundle and app metadata, do pre-install checks.
@@ -148,10 +147,10 @@ func main() {
}
// sec: should compare version string
- hlog.Verbosef("installing application %q version %q over local %q",
+ msg.Verbosef("installing application %q version %q over local %q",
bundle.ID, bundle.Version, a.Version)
} else {
- hlog.Verbosef("application %q clean installation", bundle.ID)
+ msg.Verbosef("application %q clean installation", bundle.ID)
// sec: should install credentials
}
@@ -159,7 +158,7 @@ func main() {
Setup steps for files owned by the target user.
*/
- withCacheDir(ctx, "install", []string{
+ withCacheDir(ctx, msg, "install", []string{
// export inner bundle path in the environment
"export BUNDLE=" + hst.Tmp + "/bundle",
// replace inner /etc
@@ -181,7 +180,7 @@ func main() {
}, workDir, bundle, pathSet, flagDropShell, cleanup)
if bundle.GPU {
- withCacheDir(ctx, "mesa-wrappers", []string{
+ withCacheDir(ctx, msg, "mesa-wrappers", []string{
// link nixGL mesa wrappers
"mkdir -p nix/.nixGL",
"ln -s " + bundle.Mesa + "/bin/nixGLIntel nix/.nixGL/nixGL",
@@ -193,7 +192,7 @@ func main() {
Activate home-manager generation.
*/
- withNixDaemon(ctx, "activate", []string{
+ withNixDaemon(ctx, msg, "activate", []string{
// clean up broken links
"mkdir -p .local/state/{nix,home-manager}",
"chmod -R +w .local/state/{nix,home-manager}",
@@ -261,7 +260,7 @@ func main() {
*/
if a.GPU && flagAutoDrivers {
- withNixDaemon(ctx, "nix-gl", []string{
+ withNixDaemon(ctx, msg, "nix-gl", []string{
"mkdir -p /nix/.nixGL/auto",
"rm -rf /nix/.nixGL/auto",
"export NIXPKGS_ALLOW_UNFREE=1",
@@ -316,7 +315,7 @@ func main() {
Spawn app.
*/
- mustRunApp(ctx, config, func() {})
+ mustRunApp(ctx, msg, config, func() {})
return errSuccess
}).
Flag(&flagDropShellNixGL, "s", command.BoolFlag(false), "Drop to a shell on nixGL build").
@@ -324,9 +323,9 @@ func main() {
}
c.MustParse(os.Args[1:], func(err error) {
- hlog.Verbosef("command returned %v", err)
+ msg.Verbosef("command returned %v", err)
if errors.Is(err, errSuccess) {
- hlog.BeforeExit()
+ msg.BeforeExit()
os.Exit(0)
}
})
diff --git a/cmd/hpkg/paths.go b/cmd/hpkg/paths.go
index 0964a550..ee4b7e71 100644
--- a/cmd/hpkg/paths.go
+++ b/cmd/hpkg/paths.go
@@ -9,7 +9,6 @@ import (
"hakurei.app/container"
"hakurei.app/hst"
- "hakurei.app/internal/hlog"
)
const bash = "bash"
@@ -51,8 +50,8 @@ func lookPath(file string) string {
var beforeRunFail = new(atomic.Pointer[func()])
-func mustRun(name string, arg ...string) {
- hlog.Verbosef("spawning process: %q %q", name, arg)
+func mustRun(msg container.Msg, name string, arg ...string) {
+ msg.Verbosef("spawning process: %q %q", name, arg)
cmd := exec.Command(name, arg...)
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
if err := cmd.Run(); err != nil {
diff --git a/cmd/hpkg/proc.go b/cmd/hpkg/proc.go
index d374e13d..1bbf5b49 100644
--- a/cmd/hpkg/proc.go
+++ b/cmd/hpkg/proc.go
@@ -9,14 +9,14 @@ import (
"os"
"os/exec"
+ "hakurei.app/container"
"hakurei.app/hst"
"hakurei.app/internal"
- "hakurei.app/internal/hlog"
)
var hakureiPath = internal.MustHakureiPath()
-func mustRunApp(ctx context.Context, config *hst.Config, beforeFail func()) {
+func mustRunApp(ctx context.Context, msg container.Msg, config *hst.Config, beforeFail func()) {
var (
cmd *exec.Cmd
st io.WriteCloser
@@ -26,10 +26,10 @@ func mustRunApp(ctx context.Context, config *hst.Config, beforeFail func()) {
beforeFail()
log.Fatalf("cannot pipe: %v", err)
} else {
- if hlog.Load() {
- cmd = exec.CommandContext(ctx, hakureiPath, "-v", "app", "3")
+ if msg.IsVerbose() {
+ cmd = exec.CommandContext(ctx, hakureiPath.String(), "-v", "app", "3")
} else {
- cmd = exec.CommandContext(ctx, hakureiPath, "app", "3")
+ cmd = exec.CommandContext(ctx, hakureiPath.String(), "app", "3")
}
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
cmd.ExtraFiles = []*os.File{r}
@@ -51,7 +51,8 @@ func mustRunApp(ctx context.Context, config *hst.Config, beforeFail func()) {
var exitError *exec.ExitError
if errors.As(err, &exitError) {
beforeFail()
- internal.Exit(exitError.ExitCode())
+ msg.BeforeExit()
+ os.Exit(exitError.ExitCode())
} else {
beforeFail()
log.Fatalf("cannot wait: %v", err)
diff --git a/cmd/hpkg/with.go b/cmd/hpkg/with.go
index 1895a06f..578d5251 100644
--- a/cmd/hpkg/with.go
+++ b/cmd/hpkg/with.go
@@ -2,20 +2,21 @@ package main
import (
"context"
+ "os"
"strings"
"hakurei.app/container"
"hakurei.app/container/seccomp"
"hakurei.app/hst"
- "hakurei.app/internal"
)
func withNixDaemon(
ctx context.Context,
+ msg container.Msg,
action string, command []string, net bool, updateConfig func(config *hst.Config) *hst.Config,
app *appInfo, pathSet *appPathSet, dropShell bool, beforeFail func(),
) {
- mustRunAppDropShell(ctx, updateConfig(&hst.Config{
+ mustRunAppDropShell(ctx, msg, updateConfig(&hst.Config{
ID: app.ID,
Path: pathShell,
@@ -61,9 +62,10 @@ func withNixDaemon(
func withCacheDir(
ctx context.Context,
+ msg container.Msg,
action string, command []string, workDir *container.Absolute,
app *appInfo, pathSet *appPathSet, dropShell bool, beforeFail func()) {
- mustRunAppDropShell(ctx, &hst.Config{
+ mustRunAppDropShell(ctx, msg, &hst.Config{
ID: app.ID,
Path: pathShell,
@@ -97,12 +99,13 @@ func withCacheDir(
}, dropShell, beforeFail)
}
-func mustRunAppDropShell(ctx context.Context, config *hst.Config, dropShell bool, beforeFail func()) {
+func mustRunAppDropShell(ctx context.Context, msg container.Msg, config *hst.Config, dropShell bool, beforeFail func()) {
if dropShell {
config.Args = []string{bash, "-l"}
- mustRunApp(ctx, config, beforeFail)
+ mustRunApp(ctx, msg, config, beforeFail)
beforeFail()
- internal.Exit(0)
+ msg.BeforeExit()
+ os.Exit(0)
}
- mustRunApp(ctx, config, beforeFail)
+ mustRunApp(ctx, msg, config, beforeFail)
}