container: enter init path early
Test / Create distribution (push) Successful in 59s
Test / Sandbox (push) Successful in 3m0s
Test / Hakurei (push) Successful in 4m48s
Test / Sandbox (race detector) (push) Successful in 6m6s
Test / Hakurei (race detector) (push) Successful in 7m32s
Test / ShareFS (push) Successful in 7m54s
Test / Flake checks (push) Successful in 1m19s

There is generally no use case where any setup is required before init, and requiring the explicit function call is error-prone and unnecessary. It also causes trouble with packages using a similar trick. This change moves argv0 check early and makes it an import side effect. The stub will be removed in v0.5.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
cat
2026-08-25 14:53:29 +09:00
parent ac7abbbe3f
commit 1c3761bb53
22 changed files with 32 additions and 72 deletions
-4
View File
@@ -46,7 +46,6 @@ import (
"os/signal"
"syscall"
"hakurei.app/container"
"hakurei.app/ext"
"hakurei.app/message"
)
@@ -59,9 +58,6 @@ var license string
type earlyHardeningErrs struct{ yamaLSM, dumpable error }
func main() {
// early init path, skips root check and duplicate PR_SET_DUMPABLE
container.TryArgv0(nil)
log.SetFlags(0)
log.SetPrefix("hakurei: ")
msg := message.New(log.Default())
-3
View File
@@ -37,7 +37,6 @@ import (
"hakurei.app/check"
"hakurei.app/command"
"hakurei.app/container"
"hakurei.app/ext"
"hakurei.app/fhs"
"hakurei.app/internal/pkg"
@@ -67,8 +66,6 @@ func writeFileExcl(name string, data []byte, perm os.FileMode) error {
}
func main() {
container.TryArgv0(nil)
log.SetFlags(0)
log.SetPrefix("mbf: ")
msg := message.New(log.Default())
-1
View File
@@ -287,7 +287,6 @@ func unsafeAddArgument(args *fuseArgs, arg string) {
func _main(s ...string) (exitCode int) {
msg := message.New(log.Default())
container.TryArgv0(msg)
runtime.LockOSThread()
// don't mask creation mode, kernel already did that
-3
View File
@@ -1,15 +1,12 @@
package container
import (
"encoding/gob"
"fmt"
"hakurei.app/check"
"hakurei.app/fhs"
)
func init() { gob.Register(new(AutoEtcOp)) }
// Etc is a helper for appending [AutoEtcOp] to [Ops].
func (f *Ops) Etc(host *check.Absolute, prefix string) *Ops {
e := &AutoEtcOp{prefix}
-3
View File
@@ -1,7 +1,6 @@
package container
import (
"encoding/gob"
"fmt"
"hakurei.app/check"
@@ -9,8 +8,6 @@ import (
"hakurei.app/message"
)
func init() { gob.Register(new(AutoRootOp)) }
// Root is a helper for appending [AutoRootOp] to [Ops].
func (f *Ops) Root(host *check.Absolute, flags int) *Ops {
*f = append(*f, &AutoRootOp{host, flags, nil})
-5
View File
@@ -37,9 +37,6 @@ import (
// 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"
@@ -876,8 +873,6 @@ var (
var helperCommands []func(c command.Command)
func TestMain(m *testing.M) {
container.TryArgv0(nil)
if os.Getenv(envDoCheck) == "1" {
c := command.New(os.Stderr, log.Printf, "helper", func(args []string) error {
log.SetFlags(0)
+30 -10
View File
@@ -2,6 +2,7 @@ package container
import (
"context"
"encoding/gob"
"errors"
"fmt"
"log"
@@ -130,7 +131,7 @@ type initParams struct {
Verbose bool
}
// Init is called by [TryArgv0] if the current process is the container init.
// Init is called if the current process is the container init.
func Init(msg message.Msg) { initEntrypoint(direct{}, msg) }
func initEntrypoint(k syscallDispatcher, msg message.Msg) {
@@ -642,18 +643,37 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
// initName is the prefix used by log.std in the init process.
const initName = "init"
// TryArgv0 calls [Init] if the last element of argv0 is "init".
// If a nil msg is passed, the system logger is used instead.
func TryArgv0(msg message.Msg) {
if msg == nil {
log.SetPrefix(initName + ": ")
log.SetFlags(0)
msg = message.New(log.Default())
var _ = func() struct{} {
for _, v := range []any{
(*AutoEtcOp)(nil),
(*AutoRootOp)(nil),
(*BindMountOp)(nil),
(*DaemonOp)(nil),
(*MkdirOp)(nil),
(*MountDevOp)(nil),
(*MountOverlayOp)(nil),
(*MountProcOp)(nil),
(*MountTmpfsOp)(nil),
(*RemountOp)(nil),
(*SymlinkOp)(nil),
(*TmpfileOp)(nil),
} {
gob.Register(v)
}
if len(os.Args) > 0 && filepath.Base(os.Args[0]) == initName {
if len(os.Args) == 1 && filepath.Base(os.Args[0]) == initName {
log.SetPrefix(initName + ": ")
log.SetFlags(0)
msg := message.New(log.Default())
Init(msg)
msg.BeforeExit()
os.Exit(0)
}
}
return struct{}{}
}()
// TryArgv0 is a noop.
//
// Deprecated: init is now implemented as an import side effect.
func TryArgv0(_ message.Msg) {}
-3
View File
@@ -1,7 +1,6 @@
package container
import (
"encoding/gob"
"fmt"
"os"
"syscall"
@@ -10,8 +9,6 @@ import (
"hakurei.app/container/std"
)
func init() { gob.Register(new(BindMountOp)) }
// Bind is a helper for appending [BindMountOp] to [Ops].
func (f *Ops) Bind(source, target *check.Absolute, flags int) *Ops {
*f = append(*f, &BindMountOp{nil, source, target, flags})
-3
View File
@@ -2,7 +2,6 @@ package container
import (
"context"
"encoding/gob"
"errors"
"fmt"
"os"
@@ -16,8 +15,6 @@ import (
"hakurei.app/fhs"
)
func init() { gob.Register(new(DaemonOp)) }
const (
// daemonTimeout is the duration a [DaemonOp] is allowed to block before the
// [DaemonOp.Target] marker becomes available.
-3
View File
@@ -1,7 +1,6 @@
package container
import (
"encoding/gob"
"fmt"
"path/filepath"
. "syscall"
@@ -10,8 +9,6 @@ import (
"hakurei.app/fhs"
)
func init() { gob.Register(new(MountDevOp)) }
// Dev appends an [Op] that mounts a subset of host /dev.
func (f *Ops) Dev(target *check.Absolute, mqueue bool) *Ops {
*f = append(*f, &MountDevOp{target, mqueue, false})
-3
View File
@@ -1,15 +1,12 @@
package container
import (
"encoding/gob"
"fmt"
"os"
"hakurei.app/check"
)
func init() { gob.Register(new(MkdirOp)) }
// Mkdir is a helper for appending [MkdirOp] to [Ops].
func (f *Ops) Mkdir(name *check.Absolute, perm os.FileMode) *Ops {
*f = append(*f, &MkdirOp{name, perm})
-3
View File
@@ -1,7 +1,6 @@
package container
import (
"encoding/gob"
"fmt"
"slices"
@@ -19,8 +18,6 @@ const (
intermediatePatternOverlayWork = "overlay.work.*"
)
func init() { gob.Register(new(MountOverlayOp)) }
const (
// OverlayEphemeralUnexpectedUpper is set when [MountOverlayOp.Work] is nil
// and [MountOverlayOp.Upper] holds an unexpected value.
-3
View File
@@ -1,7 +1,6 @@
package container
import (
"encoding/gob"
"fmt"
"syscall"
@@ -14,8 +13,6 @@ const (
intermediatePatternTmpfile = "tmp.*"
)
func init() { gob.Register(new(TmpfileOp)) }
// Place is a helper for appending [TmpfileOp] to [Ops].
func (f *Ops) Place(name *check.Absolute, data []byte) *Ops {
*f = append(*f, &TmpfileOp{name, data})
-3
View File
@@ -1,15 +1,12 @@
package container
import (
"encoding/gob"
"fmt"
. "syscall"
"hakurei.app/check"
)
func init() { gob.Register(new(MountProcOp)) }
// Proc is a helper for appending [MountProcOp] to [Ops].
func (f *Ops) Proc(target *check.Absolute) *Ops {
*f = append(*f, &MountProcOp{target})
-3
View File
@@ -1,14 +1,11 @@
package container
import (
"encoding/gob"
"fmt"
"hakurei.app/check"
)
func init() { gob.Register(new(RemountOp)) }
// Remount is a helper for appending [RemountOp] to [Ops].
func (f *Ops) Remount(target *check.Absolute, flags uintptr) *Ops {
*f = append(*f, &RemountOp{target, flags})
-3
View File
@@ -1,15 +1,12 @@
package container
import (
"encoding/gob"
"fmt"
"path/filepath"
"hakurei.app/check"
)
func init() { gob.Register(new(SymlinkOp)) }
// Link appends an [Op] that creates a symlink in the container filesystem.
func (f *Ops) Link(target *check.Absolute, linkName string, dereference bool) *Ops {
*f = append(*f, &SymlinkOp{target, linkName, dereference})
-3
View File
@@ -1,7 +1,6 @@
package container
import (
"encoding/gob"
"fmt"
"math"
"os"
@@ -11,8 +10,6 @@ import (
"hakurei.app/check"
)
func init() { gob.Register(new(MountTmpfsOp)) }
type TmpfsSizeError int
func (e TmpfsSizeError) Error() string {
+1 -2
View File
@@ -4,8 +4,7 @@ import (
"os"
"testing"
"hakurei.app/container"
"hakurei.app/internal/helper"
)
func TestMain(m *testing.M) { container.TryArgv0(nil); helper.InternalHelperStub(); os.Exit(m.Run()) }
func TestMain(m *testing.M) { helper.InternalHelperStub(); os.Exit(m.Run()) }
+1 -2
View File
@@ -4,8 +4,7 @@ import (
"os"
"testing"
"hakurei.app/container"
"hakurei.app/internal/helper"
)
func TestMain(m *testing.M) { container.TryArgv0(nil); helper.InternalHelperStub(); os.Exit(m.Run()) }
func TestMain(m *testing.M) { helper.InternalHelperStub(); os.Exit(m.Run()) }
-2
View File
@@ -85,8 +85,6 @@ func newRContext(tb testing.TB, c *pkg.Cache) *pkg.RContext {
return &r
}
func TestMain(m *testing.M) { container.TryArgv0(nil); os.Exit(m.Run()) }
// overrideIdent overrides the ID method of [Artifact].
type overrideIdent struct {
id pkg.ID
-3
View File
@@ -10,7 +10,6 @@ import (
"testing"
"hakurei.app/check"
"hakurei.app/container"
"hakurei.app/internal/pkg"
"hakurei.app/internal/rosa"
"hakurei.app/message"
@@ -28,8 +27,6 @@ var (
)
func TestMain(m *testing.M) {
container.TryArgv0(nil)
code := m.Run()
if buildTestCache != nil {
buildTestCacheCancel()
-4
View File
@@ -2,12 +2,10 @@ package ldd_test
import (
"errors"
"os"
"os/exec"
"testing"
"hakurei.app/check"
"hakurei.app/container"
"hakurei.app/ldd"
"hakurei.app/message"
)
@@ -42,5 +40,3 @@ func TestExec(t *testing.T) {
}
})
}
func TestMain(m *testing.M) { container.TryArgv0(nil); os.Exit(m.Run()) }