diff options
| author | Ophestra <cat@gensokyo.uk> | 2026-05-07 18:04:59 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2026-05-07 19:43:06 +0900 |
| commit | 8d72b9e5bd8d99cbd32ce0ebe2f5da8e9b1d88ce (patch) | |
| tree | 403a00ccd0d1ed98795a4e739b72f2de12b34102 /internal/pkg/exec.go | |
| parent | 8a3c3d145a560b91da1d9bce9070c20289b63502 (diff) | |
internal/pkg: optionally register binfmt
This transparently supports curing foreign exec artifacts.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/pkg/exec.go')
| -rw-r--r-- | internal/pkg/exec.go | 66 |
1 files changed, 63 insertions, 3 deletions
diff --git a/internal/pkg/exec.go b/internal/pkg/exec.go index d0d0b366..b91299dd 100644 --- a/internal/pkg/exec.go +++ b/internal/pkg/exec.go @@ -9,8 +9,10 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "slices" "strconv" + "sync" "syscall" "time" "unique" @@ -94,6 +96,32 @@ func MustPath(pathname string, writable bool, a ...Artifact) ExecPath { return ExecPath{check.MustAbs(pathname), a, writable} } +var ( + binfmt map[string]container.BinfmtEntry + binfmtMu sync.RWMutex +) + +// RegisterArch arranges for [KindExec] and [KindExecNet] to support a new +// architecture via a binfmt_misc entry. Each architecture must be registered +// at most once. +func RegisterArch(arch string, e container.BinfmtEntry) { + if arch == "" { + panic(UnsupportedArchError(arch)) + } + + binfmtMu.Lock() + defer binfmtMu.Unlock() + + if binfmt == nil { + binfmt = make(map[string]container.BinfmtEntry) + } + + if _, ok := binfmt[arch]; ok { + panic("attempting to register " + strconv.Quote(arch) + " twice") + } + binfmt[arch] = e +} + const ( // ExecTimeoutDefault replaces out of range [NewExec] timeout values. ExecTimeoutDefault = 15 * time.Minute @@ -110,6 +138,8 @@ type execArtifact struct { // Caller-supplied user-facing reporting name, guaranteed to be nonzero // during initialisation. name string + // Target architecture. + arch string // Caller-supplied inner mount points. paths []ExecPath @@ -178,7 +208,7 @@ func (a *execNetArtifact) Cure(f *FContext) error { // container and does not affect curing outcome. Because of this, it is omitted // from parameter data for computing identifier. func NewExec( - name string, + name, arch string, checksum *Checksum, timeout time.Duration, exclusive bool, @@ -193,13 +223,16 @@ func NewExec( if name == "" { name = "exec-" + filepath.Base(pathname.String()) } + if arch == "" { + arch = runtime.GOARCH + } if timeout <= 0 { timeout = ExecTimeoutDefault } if timeout > ExecTimeoutMax { timeout = ExecTimeoutMax } - a := execArtifact{name, paths, dir, env, pathname, args, timeout, exclusive} + a := execArtifact{name, arch, paths, dir, env, pathname, args, timeout, exclusive} if checksum == nil { return &a } @@ -211,6 +244,7 @@ func (*execArtifact) Kind() Kind { return KindExec } // Params writes paths, executable pathname and args. func (a *execArtifact) Params(ctx *IContext) { + ctx.WriteString(a.arch) ctx.WriteString(a.name) ctx.WriteUint32(uint32(len(a.paths))) @@ -257,11 +291,26 @@ func (a *execArtifact) Params(ctx *IContext) { } } +// UnsupportedArchError describes an unsupported or invalid architecture. +type UnsupportedArchError string + +func (e UnsupportedArchError) Error() string { + if e == "" { + return "invalid architecture name" + } + return "unsupported architecture " + string(e) +} + // readExecArtifact interprets IR values and returns the address of execArtifact // or execNetArtifact. func readExecArtifact(r *IRReader, net bool) Artifact { r.DiscardAll() + arch := r.ReadString() + if arch == "" { + panic(UnsupportedArchError(arch)) + } + name := r.ReadString() sz := r.ReadUint32() @@ -327,7 +376,7 @@ func readExecArtifact(r *IRReader, net bool) Artifact { } return NewExec( - name, checksumP, timeout, exclusive, dir, env, pathname, args, paths..., + name, arch, checksumP, timeout, exclusive, dir, env, pathname, args, paths..., ) } @@ -442,6 +491,17 @@ func (a *execArtifact) makeContainer( z.Env = slices.Concat(a.env, []string{EnvJobs + "=" + strconv.Itoa(jobs)}) z.Grow(len(a.paths) + 4) + if a.arch != runtime.GOARCH { + binfmtMu.RLock() + e, ok := binfmt[a.arch] + binfmtMu.RUnlock() + if !ok { + return nil, UnsupportedArchError(a.arch) + } + z.Binfmt = []container.BinfmtEntry{e} + z.InitAsRoot = true + } + for i, b := range a.paths { if i == overlayWorkIndex { if err = os.MkdirAll(work.String(), 0700); err != nil { |
