aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/pkg/exec.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/pkg/exec.go')
-rw-r--r--internal/pkg/exec.go66
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 {