if build:
with complete_step("Running prepare script in build overlay…"), mount_build_overlay(state):
bwrap(
- ["/work/prepare", "build"],
+ ["chroot", "/work/prepare", "build"],
tools=state.config.tools_tree,
apivfs=state.root,
- extra=chroot_cmd(state.root, options=options, network=True),
+ scripts=dict(chroot=chroot_cmd(state.root, options=options, network=True)),
env=dict(SRCDIR="/work/src") | state.environment,
)
shutil.rmtree(state.root / "work")
else:
with complete_step("Running prepare script…"):
bwrap(
- ["/work/prepare", "final"],
+ ["chroot", "/work/prepare", "final"],
tools=state.config.tools_tree,
apivfs=state.root,
- extra=chroot_cmd(state.root, options=options, network=True),
+ scripts=dict(chroot=chroot_cmd(state.root, options=options, network=True)),
env=dict(SRCDIR="/work/src") | state.environment,
)
shutil.rmtree(state.root / "work")
with complete_step("Running postinstall script…"):
bwrap(
- ["/work/postinst", "final"],
+ ["chroot", "/work/postinst", "final"],
tools=state.config.tools_tree,
apivfs=state.root,
- extra=chroot_cmd(
- state.root,
- options=["--bind", state.config.postinst_script, "/work/postinst"],
- network=state.config.with_network,
+ scripts=dict(
+ chroot=chroot_cmd(
+ state.root,
+ options=["--bind", state.config.postinst_script, "/work/postinst"],
+ network=state.config.with_network,
+ ),
),
env=state.environment,
)
with complete_step(f"Relabeling files using {policy} policy"):
bwrap(
- cmd=["sh", "-c", cmd],
+ cmd=["chroot", "sh", "-c", cmd],
tools=state.config.tools_tree,
apivfs=state.root,
- extra=chroot_cmd(state.root),
+ scripts=dict(chroot=chroot_cmd(state.root)),
env=state.environment,
)
# build-script output goes to stdout so we can run language servers from within mkosi
# build-scripts. See https://github.com/systemd/mkosi/pull/566 for more information.
bwrap(
- ["/work/build-script"],
+ ["chroot", "/work/build-script"],
tools=state.config.tools_tree,
apivfs=state.root,
- extra=chroot_cmd(state.root, options=options, network=state.config.with_network),
+ scripts=dict(chroot=chroot_cmd(state.root, options=options, network=state.config.with_network)),
env=env | state.environment,
stdout=sys.stdout,
)
) -> None:
bwrap(
cmd=[
+ "chroot",
"emerge",
*packages,
"--update",
],
tools=state.config.tools_tree,
apivfs=state.cache_dir / "stage3",
- extra=chroot_cmd(
- root=state.cache_dir / "stage3",
- options=[
- "--bind", state.root, "/tmp/mkosi-root",
- "--bind", state.cache_dir / "binpkgs", "/var/cache/binpkgs",
- "--bind", state.cache_dir / "distfiles", "/var/cache/distfiles",
- "--bind", state.cache_dir / "repos", "/var/db/repos",
- ],
- network=True,
+ scripts=dict(
+ chroot=chroot_cmd(
+ root=state.cache_dir / "stage3",
+ options=[
+ "--bind", state.root, "/tmp/mkosi-root",
+ "--bind", state.cache_dir / "binpkgs", "/var/cache/binpkgs",
+ "--bind", state.cache_dir / "distfiles", "/var/cache/distfiles",
+ "--bind", state.cache_dir / "repos", "/var/db/repos",
+ ],
+ network=True,
+ ),
),
env=dict(
FEATURES=" ".join([
copy_path(state.pkgmngr, stage3, preserve_owner=False, tools=state.config.tools_tree)
bwrap(
- cmd=["emerge-webrsync"],
+ cmd=["chroot", "emerge-webrsync"],
tools=state.config.tools_tree,
apivfs=stage3,
- extra=chroot_cmd(
- stage3,
- options=["--bind", state.cache_dir / "repos", "/var/db/repos"],
- network=True,
+ scripts=dict(
+ chroot=chroot_cmd(
+ stage3,
+ options=["--bind", state.cache_dir / "repos", "/var/db/repos"],
+ network=True,
+ ),
),
)
import fcntl
import importlib.resources
import os
-import stat
from collections.abc import Iterator
from pathlib import Path
from typing import Optional
from mkosi.run import bwrap
-
-
-def make_executable(path: Path) -> None:
- st = path.stat()
- os.chmod(path, st.st_mode | stat.S_IEXEC)
+from mkosi.util import make_executable
def write_resource(
import os
import pwd
import queue
+import shlex
import signal
import subprocess
import sys
import tempfile
+import textwrap
import threading
import traceback
from pathlib import Path
from mkosi.log import ARG_DEBUG, ARG_DEBUG_SHELL, die
from mkosi.types import _FILE, CompletedProcess, PathString, Popen
-from mkosi.util import InvokingUser
+from mkosi.util import InvokingUser, make_executable
CLONE_NEWNS = 0x00020000
CLONE_NEWUSER = 0x10000000
*,
tools: Optional[Path] = None,
apivfs: Optional[Path] = None,
+ scripts: Mapping[str, Sequence[PathString]] = {},
) -> Iterator[list[PathString]]:
cmdline: list[PathString] = [
"bwrap",
else:
chmod = ":"
- with tempfile.TemporaryDirectory(dir="/var/tmp", prefix="mkosi-var-tmp") as var_tmp:
+ with tempfile.TemporaryDirectory(dir="/var/tmp", prefix="mkosi-var-tmp") as var_tmp,\
+ tempfile.TemporaryDirectory(dir="/tmp", prefix="mkosi-scripts") as d:
+
+ for name, cmd in scripts.items():
+ # Make sure we don't end up in a recursive loop when we name a script after the binary it execs
+ # by removing the scripts directory from the PATH when we execute a script.
+ (Path(d) / name).write_text(
+ textwrap.dedent(
+ f"""\
+ #!/bin/sh
+ PATH="$(echo $PATH | tr ':' '\n' | grep -v {Path(d)} | tr '\n' ':')"
+ export PATH
+ exec {shlex.join(str(s) for s in cmd)} "$@"
+ """
+ )
+ )
+
+ make_executable(Path(d) / name)
+
+ # We modify the PATH via --setenv so that bwrap itself is looked up in PATH before we change it.
+ if tools:
+ # If a tools tree is specified, we should ignore any local modifications made to PATH as any of
+ # those binaries might not work anymore when /usr is replaced wholesale. We also make sure that
+ # both /usr/bin and /usr/sbin/ are searched so that e.g. if the host is Arch and the root is
+ # Debian we don't ignore the binaries from /usr/sbin in the Debian root. We also keep the scripts
+ # directory in PATH as all of them are interpreted and can't be messed up by replacing /usr.
+ cmdline += ["--setenv", "PATH", f"{d}:/usr/bin:/usr/sbin"]
+ else:
+ cmdline += ["--setenv", "PATH", f"{d}:{os.environ['PATH']}"]
+
if apivfs:
cmdline += [
"--bind", var_tmp, apivfs / "var/tmp",
tools: Optional[Path] = None,
apivfs: Optional[Path] = None,
log: bool = True,
- extra: Sequence[PathString] = (),
+ scripts: Mapping[str, Sequence[PathString]] = {},
# The following arguments are passed directly to run().
stdin: _FILE = None,
stdout: _FILE = None,
check: bool = True,
env: Mapping[str, PathString] = {},
) -> CompletedProcess:
- with bwrap_cmd(tools=tools, apivfs=apivfs) as bwrap:
- if tools:
- # If a tools tree is specified, we should ignore any local modifications made to PATH as any of
- # those binaries might not work anymore when /usr is replaced wholesale. We also make sure that
- # both /usr/bin and /usr/sbin/ are searched so that e.g. if the host is Arch and the root is
- # Debian we don't ignore the binaries from /usr/sbin in the Debian root.
- env = dict(PATH="/usr/bin:/usr/sbin") | env
-
+ with bwrap_cmd(tools=tools, apivfs=apivfs, scripts=scripts) as bwrap:
try:
result = run(
- [*bwrap, *extra, *cmd],
+ [*bwrap, *cmd],
text=True,
env=env,
log=False,
logging.error(f"\"{' '.join(str(s) for s in cmd)}\" returned non-zero exit code {e.returncode}.")
if ARG_DEBUG_SHELL.get():
run(
- [*bwrap, *extra, "sh"],
+ [*bwrap, "sh"],
stdin=sys.stdin,
check=False,
env=env,
import pwd
import re
import resource
+import stat
import sys
import tempfile
from collections.abc import Iterable, Iterator, Sequence
return f"{num_bytes/1024 :0.1f}K"
return f"{num_bytes}B"
+
+
+def make_executable(path: Path) -> None:
+ st = path.stat()
+ os.chmod(path, st.st_mode | stat.S_IEXEC)