]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Run binaries from ExtraSearchPaths= within tools tree
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 19 Dec 2024 11:35:47 +0000 (12:35 +0100)
committerJörg Behrmann <behrmann@physik.fu-berlin.de>
Fri, 20 Dec 2024 09:07:31 +0000 (10:07 +0100)
Until now, there was always the implicit assumption that any paths
configured with ExtraSearchPaths= contained binaries built against
the host's /usr. With that assumption, it made sense to execute binaries
found in these paths outside of the tools tree as otherwise you might
end up with missing or out of date libraries if these are not available
within the tools tree.

However, with the introduction of "mkosi sandbox", what I want to do
in systemd is to have contributors build systemd within the sandbox and
then use those binaries in mkosi to build the image. This means that the
build directory configured with ExtrasSearchPaths= suddenly contains
binaries built against the tools tree's /usr (if one is configured) instead
of the host's /usr.

Given this new use case, let's get rid of the logic to not use the tools
tree for binaries in ExtraSearchPaths=, instead, users of ExtraSearchPaths=
using a tools tree will have to make sure to use a tools tree that mostly
matches their host's /usr.

23 files changed:
mkosi/__init__.py
mkosi/archive.py
mkosi/bootloader.py
mkosi/burn.py
mkosi/config.py
mkosi/context.py
mkosi/curl.py
mkosi/distributions/debian.py
mkosi/distributions/opensuse.py
mkosi/installer/__init__.py
mkosi/installer/apt.py
mkosi/installer/dnf.py
mkosi/installer/pacman.py
mkosi/installer/rpm.py
mkosi/installer/zypper.py
mkosi/manifest.py
mkosi/partition.py
mkosi/qemu.py
mkosi/resources/man/mkosi.1.md
mkosi/run.py
mkosi/sysupdate.py
mkosi/tree.py
mkosi/vmspawn.py

index 669fb0b00663a63245af5106c637b77e8d3d74ad..e7201548ff6fde29c0f121b9219ffdb64b7c7dc2 100644 (file)
@@ -498,10 +498,7 @@ def finalize_host_scripts(
         if context.config.find_binary(binary):
             scripts[binary] = (binary, "--root", "/buildroot")
     if ukify := context.config.find_binary("ukify"):
-        # A script will always run with the tools tree mounted, so we pass binary=None to disable
-        # the conditional search logic of python_binary() depending on whether the binary is in an
-        # extra search path or not.
-        scripts["ukify"] = (python_binary(context.config, binary=None), ukify)
+        scripts["ukify"] = (python_binary(context.config), ukify)
     return finalize_scripts(context.config, scripts | dict(helpers))
 
 
@@ -542,7 +539,6 @@ def run_configure_scripts(config: Config) -> Config:
                     ["/work/configure"],
                     env=env | config.environment,
                     sandbox=config.sandbox(
-                        binary=None,
                         options=[
                             "--dir", "/work/src",
                             "--chdir", "/work/src",
@@ -617,7 +613,6 @@ def run_sync_scripts(config: Config) -> None:
                     env=env | config.environment,
                     stdin=sys.stdin,
                     sandbox=config.sandbox(
-                        binary=None,
                         network=True,
                         options=options,
                         overlay=Path(sandbox_tree),
@@ -656,7 +651,6 @@ def script_maybe_chroot_sandbox(
     with finalize_host_scripts(context, helpers) as hd:
         if script.suffix != ".chroot":
             with context.sandbox(
-                binary=None,
                 network=network,
                 options=[
                     *options,
@@ -981,7 +975,6 @@ def run_postoutput_scripts(context: Context) -> None:
                     ["/work/postoutput"],
                     env=env | context.config.environment,
                     sandbox=context.sandbox(
-                        binary=None,
                         # postoutput scripts should run as (fake) root so that file ownership is
                         # always recorded as if owned by root.
                         options=[
@@ -1034,7 +1027,6 @@ def install_tree(
             ["systemd-dissect", "--copy-from", workdir(src), "/", workdir(t)],
             env=dict(SYSTEMD_DISSECT_VERITY_EMBEDDED="no", SYSTEMD_DISSECT_VERITY_SIDECAR="no"),
             sandbox=config.sandbox(
-                binary="systemd-dissect",
                 devices=True,
                 network=True,
                 options=[
@@ -1495,7 +1487,7 @@ def run_ukify(
     (context.workspace / "cmdline").write_text(f"{' '.join(cmdline)}\x00")
 
     cmd = [
-        python_binary(context.config, binary=ukify),
+        python_binary(context.config),
         ukify,
         "build",
         *arguments,
@@ -1570,7 +1562,6 @@ def run_ukify(
         ),
         env=context.config.environment,
         sandbox=context.sandbox(
-            binary=ukify,
             options=[*opt, *options],
             devices=context.config.secure_boot_key_source.type != KeySourceType.file,
         ),
@@ -1658,7 +1649,7 @@ def build_uki(
         # new .ucode section support?
         if (
             systemd_tool_version(
-                python_binary(context.config, binary=ukify),
+                python_binary(context.config),
                 ukify,
                 sandbox=context.sandbox,
             )
@@ -1731,7 +1722,7 @@ def find_entry_token(context: Context) -> str:
             not in run(
                 ["kernel-install", "--help"],
                 stdout=subprocess.PIPE,
-                sandbox=context.sandbox(binary="kernel-install"),
+                sandbox=context.sandbox(),
             ).stdout
         )
         or systemd_tool_version("kernel-install", sandbox=context.sandbox) < "255.1"
@@ -1741,9 +1732,7 @@ def find_entry_token(context: Context) -> str:
     output = json.loads(
         run(
             ["kernel-install", "--root=/buildroot", "--json=pretty", "inspect"],
-            sandbox=context.sandbox(
-                binary="kernel-install", options=["--ro-bind", context.root, "/buildroot"]
-            ),
+            sandbox=context.sandbox(options=["--ro-bind", context.root, "/buildroot"]),
             stdout=subprocess.PIPE,
             env={"BOOT_ROOT": "/boot"},
         ).stdout
@@ -2186,7 +2175,7 @@ def maybe_compress(
             src.unlink()
 
             with dst.open("wb") as o:
-                run(cmd, stdin=i, stdout=o, sandbox=context.sandbox(binary=cmd[0]))
+                run(cmd, stdin=i, stdout=o, sandbox=context.sandbox())
 
 
 def copy_nspawn_settings(context: Context) -> None:
@@ -2336,10 +2325,7 @@ def calculate_signature_gpg(context: Context) -> None:
         run(
             cmdline,
             env=env,
-            sandbox=context.sandbox(
-                binary="gpg",
-                options=options,
-            ),
+            sandbox=context.sandbox(options=options),
         )
 
 
@@ -2358,7 +2344,6 @@ def calculate_signature_sop(context: Context) -> None:
             stdin=i,
             stdout=o,
             sandbox=context.sandbox(
-                binary=context.config.openpgp_tool,
                 options=[
                     "--bind", context.config.key, "/signing-key.pgp",
                     "--bind", context.staging, workdir(context.staging),
@@ -2563,7 +2548,7 @@ def check_ukify(
 ) -> None:
     ukify = check_tool(config, "ukify", "/usr/lib/systemd/ukify", reason=reason, hint=hint)
 
-    v = systemd_tool_version(python_binary(config, binary=ukify), ukify, sandbox=config.sandbox)
+    v = systemd_tool_version(python_binary(config), ukify, sandbox=config.sandbox)
     if v < version:
         die(
             f"Found '{ukify}' with version {v} but version {version} or newer is required to {reason}.",
@@ -2794,9 +2779,7 @@ def run_sysusers(context: Context) -> None:
     with complete_step("Generating system users"):
         run(
             ["systemd-sysusers", "--root=/buildroot"],
-            sandbox=context.sandbox(
-                binary="systemd-sysusers", options=["--bind", context.root, "/buildroot"]
-            ),
+            sandbox=context.sandbox(options=["--bind", context.root, "/buildroot"]),
         )
 
 
@@ -2827,7 +2810,6 @@ def run_tmpfiles(context: Context) -> None:
             # as success by the systemd-tmpfiles service so we handle those as success as well.
             success_exit_status=(0, 65, 73),
             sandbox=context.sandbox(
-                binary="systemd-tmpfiles",
                 options=[
                     "--bind", context.root, "/buildroot",
                     # systemd uses acl.h to parse ACLs in tmpfiles snippets which uses the host's
@@ -2853,11 +2835,11 @@ def run_preset(context: Context) -> None:
     with complete_step("Applying presets…"):
         run(
             ["systemctl", "--root=/buildroot", "preset-all"],
-            sandbox=context.sandbox(binary="systemctl", options=["--bind", context.root, "/buildroot"]),
+            sandbox=context.sandbox(options=["--bind", context.root, "/buildroot"]),
         )
         run(
             ["systemctl", "--root=/buildroot", "--global", "preset-all"],
-            sandbox=context.sandbox(binary="systemctl", options=["--bind", context.root, "/buildroot"]),
+            sandbox=context.sandbox(options=["--bind", context.root, "/buildroot"]),
         )
 
 
@@ -2872,7 +2854,7 @@ def run_hwdb(context: Context) -> None:
     with complete_step("Generating hardware database"):
         run(
             ["systemd-hwdb", "--root=/buildroot", "--usr", "--strict", "update"],
-            sandbox=context.sandbox(binary="systemd-hwdb", options=["--bind", context.root, "/buildroot"]),
+            sandbox=context.sandbox(options=["--bind", context.root, "/buildroot"]),
         )
 
     # Remove any existing hwdb in /etc in favor of the one we just put in /usr.
@@ -2891,7 +2873,7 @@ def run_firstboot(context: Context) -> None:
     if password and not hashed:
         password = run(
             ["openssl", "passwd", "-stdin", "-6"],
-            sandbox=context.sandbox(binary="openssl"),
+            sandbox=context.sandbox(),
             input=password,
             stdout=subprocess.PIPE,
         ).stdout.strip()
@@ -2925,9 +2907,7 @@ def run_firstboot(context: Context) -> None:
     with complete_step("Applying first boot settings"):
         run(
             ["systemd-firstboot", "--root=/buildroot", "--force", *options],
-            sandbox=context.sandbox(
-                binary="systemd-firstboot", options=["--bind", context.root, "/buildroot"]
-            ),
+            sandbox=context.sandbox(options=["--bind", context.root, "/buildroot"]),
         )
 
         # Initrds generally don't ship with only /usr so there's not much point in putting the
@@ -2952,7 +2932,7 @@ def run_selinux_relabel(context: Context) -> None:
     with complete_step(f"Relabeling files using {policy} policy"):
         run(
             [setfiles, "-mFr", "/buildroot", "-c", binpolicy, fc, "/buildroot"],
-            sandbox=context.sandbox(binary=setfiles, options=["--bind", context.root, "/buildroot"]),
+            sandbox=context.sandbox(options=["--bind", context.root, "/buildroot"]),
             check=context.config.selinux_relabel == ConfigFeature.enabled,
         )
 
@@ -3017,7 +2997,6 @@ def have_cache(config: Config) -> bool:
                     input=new,
                     check=False,
                     sandbox=config.sandbox(
-                        binary="diff",
                         tools=False,
                         options=["--bind", manifest, workdir(manifest)],
                     ),
@@ -3575,10 +3554,9 @@ def copy_repository_metadata(config: Config, dst: Path) -> None:
 
                 def sandbox(
                     *,
-                    binary: Optional[PathString],
                     options: Sequence[PathString] = (),
                 ) -> AbstractContextManager[list[PathString]]:
-                    return config.sandbox(binary=binary, options=[*options, *exclude])
+                    return config.sandbox(options=[*options, *exclude])
 
                 copy_tree(src, subdst, sandbox=sandbox)
 
@@ -3796,7 +3774,6 @@ def run_sandbox(args: Args, config: Config) -> None:
         env=os.environ | {"MKOSI_IN_SANDBOX": "1"},
         log=False,
         sandbox=config.sandbox(
-            binary=cmdline[0],
             devices=True,
             network=True,
             relaxed=True,
@@ -3876,7 +3853,6 @@ def run_shell(args: Args, config: Config) -> None:
                 stdin=sys.stdin,
                 env=config.environment,
                 sandbox=config.sandbox(
-                    binary="systemd-repart",
                     network=True,
                     devices=True,
                     options=["--bind", fname, workdir(fname)],
@@ -3987,7 +3963,6 @@ def run_shell(args: Args, config: Config) -> None:
             env=os.environ | config.environment,
             log=False,
             sandbox=config.sandbox(
-                binary="systemd-nspawn",
                 devices=True,
                 network=True,
                 relaxed=True,
@@ -4029,7 +4004,6 @@ def run_systemd_tool(tool: str, args: Args, config: Config) -> None:
         env=os.environ | config.environment,
         log=False,
         sandbox=config.sandbox(
-            binary=tool_path,
             network=True,
             devices=config.output_format == OutputFormat.disk,
             relaxed=True,
@@ -4050,11 +4024,10 @@ def run_serve(args: Args, config: Config) -> None:
     """Serve the output directory via a tiny HTTP server"""
 
     run(
-        [python_binary(config, binary=None), "-m", "http.server", "8081"],
+        [python_binary(config), "-m", "http.server", "8081"],
         stdin=sys.stdin,
         stdout=sys.stdout,
         sandbox=config.sandbox(
-            binary=python_binary(config, binary=None),
             network=True,
             relaxed=True,
             options=["--chdir", config.output_dir_or_cwd()],
@@ -4244,7 +4217,6 @@ def run_clean_scripts(config: Config) -> None:
                     ["/work/clean"],
                     env=env | config.environment,
                     sandbox=config.sandbox(
-                        binary=None,
                         tools=False,
                         options=[
                             "--dir", "/work/src",
index 5ab9ed3fe81a6599c93b9025e2b65f490ba2454c..be323ec7809884dc3cf78d63b916da4c15dfe438 100644 (file)
@@ -50,7 +50,6 @@ def make_tar(src: Path, dst: Path, *, sandbox: SandboxProtocol = nosandbox) -> N
             stdout=f,
             # Make sure tar uses user/group information from the root directory instead of the host.
             sandbox=sandbox(
-                binary="tar",
                 options=[
                     "--ro-bind", src, workdir(src, sandbox),
                     *finalize_passwd_symlinks(workdir(src, sandbox)),
@@ -96,7 +95,6 @@ def extract_tar(
             *options,
         ],
         sandbox=sandbox(
-            binary="tar",
             # Make sure tar uses user/group information from the root directory instead of the host.
             options=[
                 "--ro-bind", src, workdir(src, sandbox),
@@ -138,7 +136,6 @@ def make_cpio(
             input="\0".join(os.fspath(f) for f in files),
             stdout=f,
             sandbox=sandbox(
-                binary="cpio",
                 options=[
                     "--ro-bind", src, workdir(src, sandbox),
                     *finalize_passwd_symlinks(workdir(src, sandbox))
index d80aa86b64dcc3d2b37994bcadabb6829ae182c3..163837ca19205fa32df06d56f361500f1d4d0b4b 100644 (file)
@@ -232,7 +232,6 @@ def grub_mkimage(
                 *modules,
             ],
             sandbox=context.sandbox(
-                binary=mkimage,
                 options=[
                     "--bind", directory, "/grub",
                     "--ro-bind", earlyconfig.name, workdir(Path(earlyconfig.name)),
@@ -262,17 +261,11 @@ def find_signed_grub_image(context: Context) -> Optional[Path]:
     return None
 
 
-def python_binary(config: Config, *, binary: Optional[PathString]) -> PathString:
-    tools = (
-        not binary
-        or not (path := config.find_binary(binary))
-        or not any(path.is_relative_to(d) for d in config.extra_search_paths)
-    )
-
+def python_binary(config: Config) -> PathString:
     # If there's no tools tree, prefer the interpreter from MKOSI_INTERPRETER. If there is a tools
     # tree, just use the default python3 interpreter.
     exe = Path(sys.executable)
-    return "python3" if (tools and config.tools_tree) or not exe.is_relative_to("/usr") else exe
+    return "python3" if config.tools_tree or not exe.is_relative_to("/usr") else exe
 
 
 def extract_pe_section(context: Context, binary: Path, section: str, output: Path) -> Path:
@@ -297,11 +290,10 @@ def extract_pe_section(context: Context, binary: Path, section: str, output: Pat
 
     with open(output, "wb") as f:
         result = run(
-            [python_binary(context.config, binary=None)],
+            [python_binary(context.config)],
             input=pefile,
             stdout=f,
             sandbox=context.sandbox(
-                binary=python_binary(context.config, binary=None),
                 options=["--ro-bind", binary, workdir(binary)],
             ),
             success_exit_status=(0, 67),
@@ -392,7 +384,6 @@ def grub_bios_setup(context: Context, partitions: Sequence[Partition]) -> None:
                 workdir(context.staging / context.config.output_with_format),
             ],
             sandbox=context.sandbox(
-                binary=setup,
                 options=[
                     "--bind", directory, "/grub",
                     "--bind", context.staging, workdir(context.staging),
@@ -428,7 +419,7 @@ def certificate_common_name(context: Context, certificate: Path) -> str:
             "-in", workdir(certificate),
         ],
         stdout=subprocess.PIPE,
-        sandbox=context.sandbox(binary="openssl", options=["--ro-bind", certificate, workdir(certificate)]),
+        sandbox=context.sandbox(options=["--ro-bind", certificate, workdir(certificate)]),
     ).stdout  # fmt: skip
 
     for line in output.splitlines():
@@ -462,7 +453,7 @@ def run_systemd_sign_tool(
             cmdline,
             stdout=stdout,
             env={**config.environment, **env},
-            sandbox=config.sandbox(binary=cmdline[0], options=options, devices=devices),
+            sandbox=config.sandbox(options=options, devices=devices),
         )
 
     assert certificate
@@ -498,7 +489,6 @@ def run_systemd_sign_tool(
         stdout=stdout,
         env={**config.environment, **env},
         sandbox=config.sandbox(
-            binary=cmd[0],
             options=opt,
             devices=(
                 devices
@@ -537,7 +527,6 @@ def pesign_prepare(context: Context) -> None:
             ],
             stdout=f,
             sandbox=context.sandbox(
-                binary="openssl",
                 options=[
                     "--ro-bind", context.config.secure_boot_key, workdir(context.config.secure_boot_key),
                     "--ro-bind", context.config.secure_boot_certificate, workdir(context.config.secure_boot_certificate),  # noqa: E501
@@ -556,7 +545,6 @@ def pesign_prepare(context: Context) -> None:
             "-d", workdir(context.workspace / "pesign"),
         ],
         sandbox=context.sandbox(
-            binary="pk12util",
             options=[
                 "--ro-bind", context.workspace / "secure-boot.p12", workdir(context.workspace / "secure-boot.p12"),  # noqa: E501
                 "--ro-bind", context.workspace / "pesign", workdir(context.workspace / "pesign"),
@@ -626,7 +614,6 @@ def sign_efi_binary(context: Context, input: Path, output: Path) -> Path:
             ),
             env=context.config.environment,
             sandbox=context.sandbox(
-                binary="sbsign",
                 options=options,
                 devices=context.config.secure_boot_key_source.type != KeySourceType.file,
             ),
@@ -657,7 +644,6 @@ def sign_efi_binary(context: Context, input: Path, output: Path) -> Path:
             ),
             env=context.config.environment,
             sandbox=context.sandbox(
-                binary="pesign",
                 options=[
                     "--ro-bind", context.workspace / "pesign", workdir(context.workspace / "pesign"),
                     "--ro-bind", input, workdir(input),
@@ -834,7 +820,6 @@ def install_systemd_boot(context: Context) -> None:
                         "-out", workdir(context.workspace / "mkosi.der"),
                     ],
                     sandbox=context.sandbox(
-                        binary="openssl",
                         options=[
                             "--ro-bind",
                             context.config.secure_boot_certificate,
@@ -854,7 +839,6 @@ def install_systemd_boot(context: Context) -> None:
                         workdir(context.workspace / "mkosi.der"),
                     ],
                     sandbox=context.sandbox(
-                        binary="sbsiglist",
                         options=[
                             "--bind", context.workspace, workdir(context.workspace),
                             "--ro-bind", context.workspace / "mkosi.der", workdir(context.workspace / "mkosi.der"),  # noqa: E501
@@ -898,7 +882,6 @@ def install_systemd_boot(context: Context) -> None:
                             else subprocess.DEVNULL
                         ),
                         sandbox=context.sandbox(
-                            binary="sbvarsign",
                             options=options,
                             devices=context.config.secure_boot_key_source.type != KeySourceType.file,
                         ),
index 09e3abdc6bc9bcbf116e5bcaa5e558bf517332ff..07aacdc7969c69d486c06be8df5a51e88d095e89 100644 (file)
@@ -37,7 +37,6 @@ def run_burn(args: Args, config: Config) -> None:
             env=os.environ | config.environment,
             log=False,
             sandbox=config.sandbox(
-                binary="systemd-repart",
                 devices=True,
                 network=True,
                 relaxed=True,
index 958aff477a39526de3dff9e3d556a493e90276e5..5fa2507c171b18839cad8bb23aa5ba36b6be7cb5 100644 (file)
@@ -571,6 +571,7 @@ def parse_path(
     secret: bool = False,
     absolute: bool = False,
     directory: bool = False,
+    exclude: Sequence[PathString] = (),
     constants: Sequence[str] = (),
 ) -> Path:
     if value in constants:
@@ -594,6 +595,10 @@ def parse_path(
     if absolute and not path.is_absolute():
         die(f"{value} must be an absolute path")
 
+    for e in exclude:
+        if path.is_relative_to(e):
+            die(f"{path} can not be relative to {e}")
+
     if resolve:
         path = path.resolve()
 
@@ -1094,6 +1099,7 @@ def make_path_parser(
     expanduser: bool = True,
     expandvars: bool = True,
     secret: bool = False,
+    exclude: Sequence[PathString] = (),
     constants: Sequence[str] = (),
 ) -> Callable[[str], Path]:
     return functools.partial(
@@ -1103,6 +1109,7 @@ def make_path_parser(
         expanduser=expanduser,
         expandvars=expandvars,
         secret=secret,
+        exclude=exclude,
         constants=constants,
     )
 
@@ -2062,7 +2069,6 @@ class Config:
     def sandbox(
         self,
         *,
-        binary: Optional[PathString],
         network: bool = False,
         devices: bool = False,
         relaxed: bool = False,
@@ -2073,7 +2079,10 @@ class Config:
         setup: Sequence[PathString] = (),
     ) -> AbstractContextManager[list[PathString]]:
         opt: list[PathString] = [*options]
+
         if not relaxed:
+            opt += flatten(("--ro-bind", d, d) for d in self.extra_search_paths)
+
             if p := self.proxy_peer_certificate:
                 opt += ["--ro-bind", os.fspath(p), "/proxy.cacert"]
             if p := self.proxy_client_certificate:
@@ -2081,14 +2090,6 @@ class Config:
             if p := self.proxy_client_key:
                 opt += ["--ro-bind", os.fspath(p), "/proxy.clientkey"]
 
-        if (
-            binary
-            and (path := self.find_binary(binary, tools=tools))
-            and any(path.is_relative_to(d) for d in self.extra_search_paths)
-        ):
-            tools = False
-            opt += flatten(("--ro-bind", d, d) for d in self.extra_search_paths if not relaxed)
-
         return sandbox_cmd(
             network=network,
             devices=devices,
@@ -3175,7 +3176,7 @@ SETTINGS: list[ConfigSetting[Any]] = [
         long="--extra-search-path",
         metavar="PATH",
         section="Build",
-        parse=config_make_list_parser(delimiter=",", parse=make_path_parser()),
+        parse=config_make_list_parser(delimiter=",", parse=make_path_parser(exclude=["/usr"])),
         help="List of comma-separated paths to look for programs before looking in PATH",
         scope=SettingScope.universal,
     ),
@@ -5102,7 +5103,7 @@ def want_selinux_relabel(
 
     policy = run(
         ["sh", "-c", f". {workdir(selinux)} && echo $SELINUXTYPE"],
-        sandbox=config.sandbox(binary="sh", options=["--ro-bind", selinux, workdir(selinux)]),
+        sandbox=config.sandbox(options=["--ro-bind", selinux, workdir(selinux)]),
         stdout=subprocess.PIPE,
     ).stdout.strip()
     if not policy:
@@ -5144,7 +5145,7 @@ def systemd_tool_version(*tool: PathString, sandbox: SandboxProtocol = nosandbox
         run(
             [*tool, "--version"],
             stdout=subprocess.PIPE,
-            sandbox=sandbox(binary=tool[-1]),
+            sandbox=sandbox(),
         )
         .stdout.split()[2]
         .strip("()")
index a6caed275968c5a51d3c9b4ea0e7685a3db39782..2f8d32374ecfd8ca22d68d3741a27f4f14f24fc6 100644 (file)
@@ -63,14 +63,12 @@ class Context:
     def sandbox(
         self,
         *,
-        binary: Optional[PathString],
         network: bool = False,
         devices: bool = False,
         scripts: Optional[Path] = None,
         options: Sequence[PathString] = (),
     ) -> AbstractContextManager[list[PathString]]:
         return self.config.sandbox(
-            binary=binary,
             network=network,
             devices=devices,
             scripts=scripts,
index 2aa91749a3b3454d4aaf5aaca7f95b280529b94a..8d39acc970a5cde54a0684aea8b89d926bf2f2c5 100644 (file)
@@ -24,7 +24,6 @@ def curl(config: Config, url: str, output_dir: Path) -> None:
             url,
         ],
         sandbox=config.sandbox(
-            binary="curl",
             network=True,
             options=["--bind", output_dir, workdir(output_dir), *finalize_crypto_mounts(config)],
         ),
index b3daf4c92f730f83616b68323189740b37690061..90ebe5bb6f57943d81ee4ab06fc90ea9108f799c 100644 (file)
@@ -169,7 +169,7 @@ class Installer(DistributionInstaller):
                     ["dpkg-deb", "--fsys-tarfile", "/dev/stdin"],
                     stdin=i,
                     stdout=o,
-                    sandbox=context.sandbox(binary="dpkg-deb"),
+                    sandbox=context.sandbox(),
                 )
                 extract_tar(
                     Path(o.name),
@@ -298,9 +298,7 @@ def fixup_os_release(context: Context) -> None:
                     f"/{candidate}.dpkg",
                     f"/{candidate}",
                 ],
-                sandbox=context.sandbox(
-                    binary="dpkg-divert", options=["--bind", context.root, "/buildroot"]
-                ),
+                sandbox=context.sandbox(options=["--bind", context.root, "/buildroot"]),
             )
 
         newosrelease.rename(osrelease)
index fe8c6f2b42d52a45242f0226c364d37d29084b14..6b07ef53023f2d496cba8b72b075df3dd44e3145 100644 (file)
@@ -115,7 +115,6 @@ class Installer(DistributionInstaller):
                         *(key.removeprefix("file://") for key in gpgkeys),
                     ],
                     sandbox=context.sandbox(
-                        binary="rpm",
                         options=[
                             "--bind", context.root, "/buildroot",
                             *finalize_crypto_mounts(context.config),
index fc971342007ebee4f651140390ec5043b2037928..ebf858b47b44678627673bada773d6b6227df63e 100644 (file)
@@ -135,7 +135,6 @@ class PackageManager:
         options: Sequence[PathString] = (),
     ) -> AbstractContextManager[list[PathString]]:
         return context.sandbox(
-            binary=cls.executable(context.config),
             network=True,
             options=[
                 "--bind", context.root, "/buildroot",
index ae8c4b595dc09c4214c00692d921a50fbebb10c6..4c0ecafdf78a83b872826ba327b46c56c22f702a 100644 (file)
@@ -256,7 +256,6 @@ class Apt(PackageManager):
                 *(d.name for glob in PACKAGE_GLOBS for d in context.repository.glob(glob) if "deb" in glob),
             ],
             sandbox=context.sandbox(
-                binary="reprepro",
                 options=[
                     "--bind", context.repository, workdir(context.repository),
                     "--chdir", workdir(context.repository),
index aa70f756ee00af23a14c4ce243b69904859d96c2..43237877b92b91f162c4265564b4ef3b0ea57057 100644 (file)
@@ -218,9 +218,7 @@ class Dnf(PackageManager):
     def createrepo(cls, context: Context) -> None:
         run(
             ["createrepo_c", workdir(context.repository)],
-            sandbox=context.sandbox(
-                binary="createrepo_c", options=["--bind", context.repository, workdir(context.repository)]
-            ),
+            sandbox=context.sandbox(options=["--bind", context.repository, workdir(context.repository)]),
         )
 
         (context.sandbox_tree / "etc/yum.repos.d/mkosi-local.repo").write_text(
index e7db0a8313d1e280ee83836dea80431afc7608d0..e8b7b81afd6d53f74de88edf2d862fd8276002c7 100644 (file)
@@ -191,10 +191,7 @@ class Pacman(PackageManager):
                     key=lambda p: GenericVersion(Path(p).name),
                 ),
             ],
-            sandbox=context.sandbox(
-                binary="repo-add",
-                options=["--bind", context.repository, workdir(context.repository)],
-            ),
+            sandbox=context.sandbox(options=["--bind", context.repository, workdir(context.repository)]),
         )
 
         (context.sandbox_tree / "etc/mkosi-local.conf").write_text(
index f210db543a1cedac2102250c8cadc7474e801392..a50768a5228b9fa41b1b643d4341de44082397a6 100644 (file)
@@ -83,7 +83,7 @@ def setup_rpm(
     plugindir = Path(
         run(
             ["rpm", "--eval", "%{__plugindir}"],
-            sandbox=context.sandbox(binary="rpm"),
+            sandbox=context.sandbox(),
             stdout=subprocess.PIPE,
         ).stdout.strip()
     )
index 16ad76d49e4a5c2434f9fb4afe7631f5db925c41..1215aadbfaf02c491059c2f9735d45c07d9f2095 100644 (file)
@@ -139,10 +139,7 @@ class Zypper(PackageManager):
     def createrepo(cls, context: Context) -> None:
         run(
             ["createrepo_c", workdir(context.repository)],
-            sandbox=context.sandbox(
-                binary="createrepo_c",
-                options=["--bind", context.repository, workdir(context.repository)],
-            ),
+            sandbox=context.sandbox(options=["--bind", context.repository, workdir(context.repository)]),
         )
 
         (context.sandbox_tree / "etc/zypp/repos.d/mkosi-local.repo").write_text(
index d27a66200adda6786d015ed47276d9a12aee730d..2dd2fd576706383d3debef6845862ea85c5a78ec 100644 (file)
@@ -111,7 +111,7 @@ class Manifest:
             ],
             stdout=subprocess.PIPE,
             sandbox=(
-                self.context.sandbox(binary="rpm", options=["--ro-bind", self.context.root, "/buildroot"])
+                self.context.sandbox(options=["--ro-bind", self.context.root, "/buildroot"])
             ),
         )  # fmt: skip
 
@@ -158,10 +158,7 @@ class Manifest:
                     ],
                     stdout=subprocess.PIPE,
                     stderr=subprocess.DEVNULL,
-                    sandbox=self.context.sandbox(
-                        binary="rpm",
-                        options=["--ro-bind", self.context.root, "/buildroot"],
-                    ),
+                    sandbox=self.context.sandbox(options=["--ro-bind", self.context.root, "/buildroot"]),
                 )
                 changelog = c.stdout.strip()
                 source = SourcePackageManifest(srpm, changelog)
@@ -178,10 +175,7 @@ class Manifest:
                 "--showformat", r"${Package}\t${source:Package}\t${Version}\t${Architecture}\t${Installed-Size}\t${db-fsys:Last-Modified}\n",  # noqa: E501
             ],
             stdout=subprocess.PIPE,
-            sandbox=self.context.sandbox(
-                binary="dpkg-query",
-                options=["--ro-bind", self.context.root, "/buildroot"],
-            ),
+            sandbox=self.context.sandbox(options=["--ro-bind", self.context.root, "/buildroot"]),
         )  # fmt: skip
 
         packages = sorted(c.stdout.splitlines())
index 282fae37e006f8e06cd81db37058ba432683ffdc..e7eb94407aca25bc81fdb7765b6c7bf06129efa3 100644 (file)
@@ -39,7 +39,7 @@ def find_partitions(image: Path, *, sandbox: SandboxProtocol = nosandbox) -> lis
             ["systemd-repart", "--json=short", workdir(image, sandbox)],
             stdout=subprocess.PIPE,
             stderr=subprocess.DEVNULL,
-            sandbox=sandbox(binary="systemd-repart", options=["--ro-bind", image, workdir(image, sandbox)]),
+            sandbox=sandbox(options=["--ro-bind", image, workdir(image, sandbox)]),
         ).stdout
     )
     return [Partition.from_dict(d) for d in output]
index aef7310997eee1815bd17124e527e38b11d62b2e..512ff32d14f9261a666c1de1e97a2630db8cf0b8 100644 (file)
@@ -159,7 +159,7 @@ class KernelType(StrEnum):
         type = run(
             ["bootctl", "kernel-identify", workdir(path)],
             stdout=subprocess.PIPE,
-            sandbox=config.sandbox(binary="bootctl", options=["--ro-bind", path, workdir(path)]),
+            sandbox=config.sandbox(options=["--ro-bind", path, workdir(path)]),
         ).stdout.strip()
 
         try:
@@ -178,15 +178,13 @@ class OvmfConfig:
     vars_format: str
 
 
-def find_ovmf_firmware(config: Config, qemu: Path, firmware: QemuFirmware) -> Optional[OvmfConfig]:
+def find_ovmf_firmware(config: Config, firmware: QemuFirmware) -> Optional[OvmfConfig]:
     if not firmware.is_uefi():
         return None
 
-    tools = Path("/") if any(qemu.is_relative_to(d) for d in config.extra_search_paths) else config.tools()
-
-    desc = list((tools / "usr/share/qemu/firmware").glob("*"))
-    if tools == Path("/"):
-        desc += list((tools / "etc/qemu/firmware").glob("*"))
+    desc = list((config.tools() / "usr/share/qemu/firmware").glob("*"))
+    if config.tools() == Path("/"):
+        desc += list((config.tools() / "etc/qemu/firmware").glob("*"))
 
     arch = config.architecture.to_qemu()
     machine = config.architecture.default_qemu_machine()
@@ -235,7 +233,7 @@ def find_ovmf_firmware(config: Config, qemu: Path, firmware: QemuFirmware) -> Op
         logging.debug(f"Using {p.name} firmware description")
 
         return OvmfConfig(
-            description=Path("/") / p.relative_to(tools),
+            description=Path("/") / p.relative_to(config.tools()),
             firmware=Path(j["mapping"]["executable"]["filename"]),
             format=j["mapping"]["executable"]["format"],
             vars=Path(j["mapping"]["nvram-template"]["filename"]),
@@ -258,10 +256,7 @@ def start_swtpm(config: Config) -> Iterator[Path]:
                 "sha256",
                 "--config", "/dev/null",
             ],
-            sandbox=config.sandbox(
-                binary="swtpm_setup",
-                options=["--bind", state, workdir(Path(state))],
-            ),
+            sandbox=config.sandbox(options=["--bind", state, workdir(Path(state))]),
             stdout=None if ARG_DEBUG.get() else subprocess.DEVNULL,
         )  # fmt: skip
 
@@ -280,7 +275,6 @@ def start_swtpm(config: Config) -> Iterator[Path]:
                 cmdline,
                 pass_fds=(sock.fileno(),),
                 sandbox=config.sandbox(
-                    binary="swtpm",
                     options=["--bind", state, workdir(Path(state))],
                     setup=scope_cmd(
                         name=f"mkosi-swtpm-{config.machine_or_name()}",
@@ -314,9 +308,7 @@ def systemd_escape(config: Config, s: PathString, path: bool = False) -> str:
     if path:
         cmdline += ["--path"]
 
-    return run(
-        cmdline, stdout=subprocess.PIPE, sandbox=config.sandbox(binary="systemd-escape")
-    ).stdout.strip()
+    return run(cmdline, stdout=subprocess.PIPE, sandbox=config.sandbox()).stdout.strip()
 
 
 @contextlib.contextmanager
@@ -404,7 +396,6 @@ def start_virtiofsd(
             # features.
             preexec_fn=become_root_in_subuid_range if not scope and not uidmap else None,
             sandbox=config.sandbox(
-                binary=virtiofsd,
                 options=[
                     "--bind", directory, workdir(directory),
                     *(["--become-root"] if uidmap else []),
@@ -476,7 +467,7 @@ def make_nocow(config: Config, path: Path) -> None:
         ["chattr", "+C", workdir(path)],
         check=False,
         stderr=subprocess.DEVNULL if not ARG_DEBUG.get() else None,
-        sandbox=config.sandbox(binary="chattr", options=["--bind", path, workdir(path)]),
+        sandbox=config.sandbox(options=["--bind", path, workdir(path)]),
     )
 
 
@@ -537,7 +528,6 @@ def start_journal_remote(config: Config, sockfd: int) -> Iterator[None]:
             ],
             pass_fds=(sockfd,),
             sandbox=config.sandbox(
-                binary=bin,
                 options=[
                     "--bind", config.forward_journal.parent, workdir(config.forward_journal.parent),
                     "--ro-bind", f.name, "/etc/systemd/journal-remote.conf",
@@ -591,7 +581,7 @@ def copy_ephemeral(config: Config, src: Path) -> Iterator[Path]:
             if config.output_format in (OutputFormat.disk, OutputFormat.esp):
                 attr = run(
                     ["lsattr", "-l", workdir(src)],
-                    sandbox=config.sandbox(binary="lsattr", options=["--ro-bind", src, workdir(src)]),
+                    sandbox=config.sandbox(options=["--ro-bind", src, workdir(src)]),
                     stdout=subprocess.PIPE,
                 ).stdout
 
@@ -629,7 +619,7 @@ def qemu_version(config: Config, binary: Path) -> GenericVersion:
         run(
             [binary, "--version"],
             stdout=subprocess.PIPE,
-            sandbox=config.sandbox(binary=binary),
+            sandbox=config.sandbox(),
         ).stdout.split()[3]
     )
 
@@ -650,10 +640,7 @@ def generate_scratch_fs(config: Config) -> Iterator[Path]:
         run(
             [f"mkfs.{fs}", "-L", "scratch", *extra.split(), workdir(Path(scratch.name))],
             stdout=subprocess.DEVNULL,
-            sandbox=config.sandbox(
-                binary=f"mkfs.{fs}",
-                options=["--bind", scratch.name, workdir(Path(scratch.name))],
-            ),
+            sandbox=config.sandbox(options=["--bind", scratch.name, workdir(Path(scratch.name))]),
         )
         yield Path(scratch.name)
 
@@ -708,7 +695,6 @@ def finalize_firmware_variables(
                 "--loglevel", "WARNING",
             ],
             sandbox=config.sandbox(
-                binary=qemu,
                 options=[
                     "--bind", ovmf_vars.name, workdir(Path(ovmf_vars.name)),
                     "--ro-bind", ovmf.vars, workdir(ovmf.vars),
@@ -717,11 +703,8 @@ def finalize_firmware_variables(
             ),
         )  # fmt: skip
     else:
-        tools = (
-            Path("/") if any(qemu.is_relative_to(d) for d in config.extra_search_paths) else config.tools()
-        )
         vars = (
-            tools / ovmf.vars.relative_to("/")
+            config.tools() / ovmf.vars.relative_to("/")
             if config.qemu_firmware_variables == Path("microsoft") or not config.qemu_firmware_variables
             else config.qemu_firmware_variables
         )
@@ -745,7 +728,7 @@ def apply_runtime_size(config: Config, image: Path) -> None:
             "--offline=yes",
             workdir(image),
         ],
-        sandbox=config.sandbox(binary="systemd-repart", options=["--bind", image, workdir(image)]),
+        sandbox=config.sandbox(options=["--bind", image, workdir(image)]),
     )  # fmt: skip
 
 
@@ -949,7 +932,7 @@ def register_machine(config: Config, pid: int, fname: Path) -> None:
         ],  # fmt: skip
         foreground=False,
         env=os.environ | config.environment,
-        sandbox=config.sandbox(binary="busctl", relaxed=True),
+        sandbox=config.sandbox(relaxed=True),
         # systemd-machined might not be installed so let's ignore any failures unless running in debug mode.
         check=ARG_DEBUG.get(),
         stderr=None if ARG_DEBUG.get() else subprocess.DEVNULL,
@@ -1045,7 +1028,7 @@ def run_qemu(args: Args, config: Config) -> None:
                 "or provide a -kernel argument to mkosi qemu"
             )
 
-    ovmf = find_ovmf_firmware(config, qemu, firmware)
+    ovmf = find_ovmf_firmware(config, firmware)
 
     # A shared memory backend might increase ram usage so only add one if actually necessary for virtiofsd.
     shm = []
@@ -1176,7 +1159,6 @@ def run_qemu(args: Args, config: Config) -> None:
                     workdir(fname),
                 ],  # fmt: skip
                 sandbox=config.sandbox(
-                    binary="systemd-repart",
                     options=[
                         "--bind", fname.parent, workdir(fname.parent),
                         "--ro-bind", src, workdir(src),
@@ -1405,7 +1387,6 @@ def run_qemu(args: Args, config: Config) -> None:
             env=os.environ | config.environment,
             foreground=True,
             sandbox=config.sandbox(
-                binary=qemu,
                 network=True,
                 devices=True,
                 relaxed=True,
@@ -1466,7 +1447,6 @@ def run_ssh(args: Args, config: Config) -> None:
         env=os.environ | config.environment,
         log=False,
         sandbox=config.sandbox(
-            binary="ssh",
             network=True,
             devices=True,
             relaxed=True,
index 29f8f7fbbae9c1a14d2073d1e637f7724efa8cf0..79f199f7bad2202c4633dbca02ac2cacb12d776b 100644 (file)
@@ -1228,8 +1228,12 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`,
     but the `mkosi.tools/` directory is found in the local directory it is
     automatically used for this purpose with the root directory as target.
 
-    Note if a binary is found in any of the paths configured with
-    `ExtraSearchPaths=`, the binary will be executed on the host.
+    Note that binaries found in any of the paths configured with
+    `ExtraSearchPaths=` will be executed with `/usr/` from the tools
+    tree instead of from the host. If the host distribution or release
+    does not match the tools tree distribution or release respectively,
+    this might result in failures when trying to execute binaries from
+    any of the extra search paths.
 
     If set to `default`, mkosi will automatically add an extra tools tree
     image and use it as the tools tree.
index b777fffb733d239aed81345a01efe19f78f9b0d9..042921b24927d084d4918c60b3212d261c6349f7 100644 (file)
@@ -397,14 +397,12 @@ class SandboxProtocol(Protocol):
     def __call__(
         self,
         *,
-        binary: Optional[PathString],
         options: Sequence[PathString] = (),
     ) -> AbstractContextManager[list[PathString]]: ...
 
 
 def nosandbox(
     *,
-    binary: Optional[PathString],
     options: Sequence[PathString] = (),
 ) -> AbstractContextManager[list[PathString]]:
     return contextlib.nullcontext([])
index f23c2edfa6e9e95c497addd8481ef42e48af44bc..9b9a8a00bbff11dcff19a040bb991037c0a56d2e 100644 (file)
@@ -51,7 +51,6 @@ def run_sysupdate(args: Args, config: Config) -> None:
             env=os.environ | config.environment,
             log=False,
             sandbox=config.sandbox(
-                binary=sysupdate,
                 devices=True,
                 network=True,
                 relaxed=True,
index df3e8247c1ce19e96ee8e2af5899e76993ff5672..da6806776f82bd82a26c1ceb244eae41267ef576 100644 (file)
@@ -26,7 +26,7 @@ def cp_version(*, sandbox: SandboxProtocol = nosandbox) -> GenericVersion:
     return GenericVersion(
         run(
             ["cp", "--version"],
-            sandbox=sandbox(binary="cp"),
+            sandbox=sandbox(),
             stdout=subprocess.PIPE,
         )
         .stdout.splitlines()[0]
@@ -52,7 +52,7 @@ def make_tree(
     if use_subvolumes != ConfigFeature.disabled:
         result = run(
             ["btrfs", "subvolume", "create", workdir(path, sandbox)],
-            sandbox=sandbox(binary="btrfs", options=["--bind", path.parent, workdir(path.parent, sandbox)]),
+            sandbox=sandbox(options=["--bind", path.parent, workdir(path.parent, sandbox)]),
             check=use_subvolumes == ConfigFeature.enabled,
         ).returncode
     else:
@@ -117,7 +117,7 @@ def copy_tree(
         if src.is_dir():
             cmdline += ["--no-target-directory"]
 
-        run(cmdline, sandbox=sandbox(binary="cp", options=options))
+        run(cmdline, sandbox=sandbox(options=options))
 
     # Subvolumes always have inode 256 so we can use that to check if a directory is a subvolume.
     if (
@@ -138,7 +138,7 @@ def copy_tree(
     result = run(
         ["btrfs", "subvolume", "snapshot", workdir(src, sandbox), workdir(dst, sandbox)],
         check=use_subvolumes == ConfigFeature.enabled,
-        sandbox=sandbox(binary="btrfs", options=options),
+        sandbox=sandbox(options=options),
     ).returncode
 
     if result != 0:
@@ -161,7 +161,6 @@ def rmtree(*paths: Path, sandbox: SandboxProtocol = nosandbox) -> None:
             ["btrfs", "subvolume", "delete", *(workdir(p, sandbox) for p in subvolumes)],
             check=False,
             sandbox=sandbox(
-                binary="btrfs",
                 options=flatten(("--bind", p.parent, workdir(p.parent, sandbox)) for p in subvolumes),
             ),
             stdout=subprocess.DEVNULL if not ARG_DEBUG.get() else None,
@@ -173,7 +172,6 @@ def rmtree(*paths: Path, sandbox: SandboxProtocol = nosandbox) -> None:
         run(
             ["rm", "-rf", "--", *(workdir(p, sandbox) for p in filtered)],
             sandbox=sandbox(
-                binary="rm",
                 options=flatten(("--bind", p.parent, workdir(p.parent, sandbox)) for p in filtered),
             ),
         )
index 452e119ceae50e819a89943f69a0810685d8cb72..95726168ef1732a1b481a4445840a00d70a5e185 100644 (file)
@@ -115,7 +115,6 @@ def run_vmspawn(args: Args, config: Config) -> None:
             env=os.environ | config.environment,
             log=False,
             sandbox=config.sandbox(
-                binary=cmdline[0],
                 network=True,
                 devices=True,
                 relaxed=True,