mirror of
https://gitlab.com/kupfer/kupferbootstrap.git
synced 2025-02-22 13:15:44 -05:00
125 lines
4.5 KiB
Python
125 lines
4.5 KiB
Python
# modifed from pmbootstrap's binfmt.py, Copyright 2018 Oliver Smith, GPL-licensed
|
|
|
|
import os
|
|
import logging
|
|
|
|
from typing import Optional
|
|
|
|
from chroot.abstract import Chroot
|
|
from constants import Arch, QEMU_ARCHES
|
|
from exec.cmd import run_root_cmd, CompletedProcess
|
|
from utils import mount
|
|
|
|
|
|
def binfmt_info(chroot: Optional[Chroot] = None):
|
|
# Parse the info file
|
|
full = {}
|
|
info = "/usr/lib/binfmt.d/qemu-static.conf"
|
|
if chroot:
|
|
info = chroot.get_path(info)
|
|
logging.debug("parsing: " + info)
|
|
with open(info, "r") as handle:
|
|
for line in handle:
|
|
if line.startswith('#') or ":" not in line:
|
|
continue
|
|
splitted = line.split(":")
|
|
result = {
|
|
# _ = splitted[0] # empty
|
|
'name': splitted[1],
|
|
'type': splitted[2],
|
|
'offset': splitted[3],
|
|
'magic': splitted[4],
|
|
'mask': splitted[5],
|
|
'interpreter': splitted[6],
|
|
'flags': splitted[7],
|
|
'line': line,
|
|
}
|
|
if not result['name'].startswith('qemu-'):
|
|
logging.fatal(f'Unknown binfmt handler "{result["name"]}"')
|
|
logging.debug(f'binfmt line: {line}')
|
|
continue
|
|
arch = ''.join(result['name'].split('-')[1:])
|
|
full[arch] = result
|
|
|
|
return full
|
|
|
|
|
|
def is_arch_known(arch: Arch, raise_exception: bool = False, action: Optional[str] = None) -> bool:
|
|
if arch not in QEMU_ARCHES:
|
|
if raise_exception:
|
|
raise Exception(f'binfmt{f".{action}()" if action else ""}: unknown arch {arch} (not in QEMU_ARCHES)')
|
|
return False
|
|
return True
|
|
|
|
|
|
def binfmt_is_registered(arch: Arch, chroot: Optional[Chroot] = None) -> bool:
|
|
is_arch_known(arch, True, 'is_registered')
|
|
qemu_arch = QEMU_ARCHES[arch]
|
|
path = "/proc/sys/fs/binfmt_misc/qemu-" + qemu_arch
|
|
binfmt_ensure_mounted(chroot)
|
|
if chroot:
|
|
path = chroot.get_path(path)
|
|
return os.path.exists(path)
|
|
|
|
|
|
def binfmt_ensure_mounted(chroot: Optional[Chroot] = None):
|
|
binfmt_path = '/proc/sys/fs/binfmt_misc'
|
|
register_path = binfmt_path + '/register'
|
|
if chroot:
|
|
register_path = chroot.get_path(register_path)
|
|
if not os.path.exists(register_path):
|
|
logging.info('mounting binfmt_misc')
|
|
result = (chroot.mount if chroot else mount)('binfmt_misc', binfmt_path, options=[], fs_type='binfmt_misc') # type: ignore[operator]
|
|
if (isinstance(result, CompletedProcess) and result.returncode != 0) or not result:
|
|
raise Exception(f'Failed mounting binfmt_misc to {binfmt_path}')
|
|
|
|
|
|
def binfmt_register(arch: Arch, chroot: Optional[Chroot] = None):
|
|
binfmt_path = '/proc/sys/fs/binfmt_misc'
|
|
register_path = binfmt_path + '/register'
|
|
is_arch_known(arch, True, 'register')
|
|
qemu_arch = QEMU_ARCHES[arch]
|
|
if binfmt_is_registered(arch, chroot=chroot):
|
|
return
|
|
|
|
lines = binfmt_info(chroot=chroot)
|
|
|
|
_runcmd = run_root_cmd
|
|
if chroot:
|
|
_runcmd = chroot.run_cmd
|
|
chroot.activate()
|
|
|
|
binfmt_ensure_mounted(chroot)
|
|
|
|
# Build registration string
|
|
# https://en.wikipedia.org/wiki/Binfmt_misc
|
|
# :name:type:offset:magic:mask:interpreter:flags
|
|
info = lines[qemu_arch]
|
|
code = info['line']
|
|
|
|
if arch == os.uname().machine:
|
|
logging.fatal("Attempted to register qemu binfmt for host architecture, skipping!")
|
|
return
|
|
|
|
# Register in binfmt_misc
|
|
logging.info(f"Registering qemu binfmt ({arch})")
|
|
_runcmd(f'echo "{code}" > "{register_path}" 2>/dev/null') # use path without chroot path prefix
|
|
if not binfmt_is_registered(arch, chroot=chroot):
|
|
logging.debug(f'binfmt line: {code}')
|
|
raise Exception(f'Failed to register qemu-user for {arch} with binfmt_misc, {binfmt_path}/{info["name"]} not found')
|
|
|
|
|
|
def binfmt_unregister(arch, chroot: Optional[Chroot] = None):
|
|
is_arch_known(arch, True, 'unregister')
|
|
qemu_arch = QEMU_ARCHES[arch]
|
|
binfmt_ensure_mounted(chroot)
|
|
binfmt_file = "/proc/sys/fs/binfmt_misc/qemu-" + qemu_arch
|
|
if chroot:
|
|
binfmt_file = chroot.get_path(binfmt_file)
|
|
if not os.path.exists(binfmt_file):
|
|
logging.debug(f"qemu binfmt for {arch} not registered")
|
|
return
|
|
logging.info(f"Unregistering qemu binfmt ({arch})")
|
|
run_root_cmd(f"echo -1 > {binfmt_file}")
|
|
if binfmt_is_registered(arch, chroot=chroot):
|
|
raise Exception(f'Failed to UNregister qemu-user for {arch} with binfmt_misc, {chroot=}')
|