mirror of
https://github.com/containers/podman.git
synced 2025-08-01 07:40:22 +08:00

Add podman farm build command that sends out builds to nodes defined in the farm, builds the images on the farm nodes, and pulls them back to the local machine to create a manifest list. Signed-off-by: Urvashi Mohnani <umohnani@redhat.com>
170 lines
4.4 KiB
Go
170 lines
4.4 KiB
Go
//go:build !remote
|
|
// +build !remote
|
|
|
|
package emulation
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// registeredBinfmtMisc walks /proc/sys/fs/binfmt_misc and iterates through a
|
|
// list of known ELF header values to see if there's an emulator registered for
|
|
// them. Returns the list of emulated targets (which may be empty), or an
|
|
// error if something unexpected happened.
|
|
func registeredBinfmtMisc() ([]string, error) {
|
|
var registered []string
|
|
globalEnabled := false
|
|
err := filepath.WalkDir("/proc/sys/fs/binfmt_misc", func(path string, d fs.DirEntry, err error) error {
|
|
if filepath.Base(path) == "register" { // skip this one
|
|
return nil
|
|
}
|
|
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
return err
|
|
}
|
|
info, err := d.Info()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !info.Mode().IsRegular() {
|
|
return nil // skip the directory itself
|
|
}
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
if filepath.Base(path) == "status" {
|
|
b, err := io.ReadAll(f)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
status := strings.TrimSpace(string(b))
|
|
switch status {
|
|
case "disabled":
|
|
globalEnabled = false
|
|
case "enabled":
|
|
globalEnabled = true
|
|
default:
|
|
return fmt.Errorf("unrecognized binfmt_misc status value %q in %q", status, path)
|
|
}
|
|
return nil
|
|
}
|
|
offset, magic, mask, err := parseBinfmtMisc(path, f)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if offset < 0 {
|
|
return nil
|
|
}
|
|
for platform, headers := range getKnownELFPlatformHeaders() {
|
|
for _, header := range headers {
|
|
if magicMatch(header, offset, mask, magic) {
|
|
registered = append(registered, platform)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
if !globalEnabled {
|
|
return nil, nil
|
|
}
|
|
sort.Strings(registered)
|
|
return registered, err
|
|
}
|
|
|
|
// magicMatch compares header, starting at the specified offset, masked with
|
|
// mask, against the magic value
|
|
func magicMatch(header []byte, offset int, mask, magic []byte) bool {
|
|
mismatch := 0
|
|
for i := offset; i < offset+len(magic); i++ {
|
|
if i >= len(header) {
|
|
break
|
|
}
|
|
m := magic[i-offset]
|
|
if len(mask) > i-offset {
|
|
m &= mask[i-offset]
|
|
}
|
|
if header[i] != m {
|
|
// mismatch
|
|
break
|
|
}
|
|
mismatch = i + 1
|
|
}
|
|
return mismatch >= offset+len(magic)
|
|
}
|
|
|
|
// parseBinfmtMisc parses a binfmt_misc registry entry. It returns the offset,
|
|
// magic, and mask values, or an error if there was an error parsing the data.
|
|
// If the returned offset is negative, the entry was disabled or should be
|
|
// non-fatally ignored for some other reason.
|
|
func parseBinfmtMisc(path string, r io.Reader) (int, []byte, []byte, error) {
|
|
offset := 0
|
|
magicString, maskString := "", ""
|
|
scanner := bufio.NewScanner(r)
|
|
for scanner.Scan() {
|
|
text := scanner.Text()
|
|
if strings.TrimSpace(text) == "" {
|
|
continue
|
|
}
|
|
fields := strings.Fields(text)
|
|
switch fields[0] {
|
|
case "disabled":
|
|
return -1, nil, nil, nil // we should ignore this specific one
|
|
case "enabled": // keep scanning this entry
|
|
case "interpreter": // good, but not something we need to record
|
|
case "offset":
|
|
if len(fields) != 2 {
|
|
return -1, nil, nil, fmt.Errorf("invalid format for %q in %q", text, path)
|
|
}
|
|
offset64, err := strconv.ParseInt(fields[1], 10, 8)
|
|
if err != nil {
|
|
return -1, nil, nil, fmt.Errorf("invalid offset %q in %q", fields[1], path)
|
|
}
|
|
offset = int(offset64)
|
|
case "magic":
|
|
if len(fields) != 2 {
|
|
return -1, nil, nil, fmt.Errorf("invalid format for %q in %q", text, path)
|
|
}
|
|
magicString = fields[1]
|
|
case "mask":
|
|
if len(fields) != 2 {
|
|
return -1, nil, nil, fmt.Errorf("invalid format for %q in %q", text, path)
|
|
}
|
|
maskString = fields[1]
|
|
case "flags", "flags:":
|
|
if len(fields) != 2 {
|
|
return -1, nil, nil, fmt.Errorf("invalid format for %q in %q", text, path)
|
|
}
|
|
if !strings.Contains(fields[1], "F") { // won't work in other mount namespaces, so ignore it
|
|
return -1, nil, nil, nil
|
|
}
|
|
default:
|
|
return -1, nil, nil, fmt.Errorf("unrecognized field %q in %q", fields[0], path)
|
|
}
|
|
continue
|
|
}
|
|
if magicString == "" || maskString == "" { // entry is missing some info we need here
|
|
return -1, nil, nil, nil
|
|
}
|
|
magic, err := hex.DecodeString(magicString)
|
|
if err != nil {
|
|
return -1, nil, nil, fmt.Errorf("invalid magic %q in %q", magicString, path)
|
|
}
|
|
mask, err := hex.DecodeString(maskString)
|
|
if err != nil {
|
|
return -1, nil, nil, fmt.Errorf("invalid mask %q in %q", maskString, path)
|
|
}
|
|
return offset, magic, mask, nil
|
|
}
|