We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Debian tools which use /etc/alternatives break
Regression caused by https://github.com/systemd/mkosi/pull/2201, already reported in https://github.com/systemd/mkosi/issues/2206.

When putting a keyring in `/etc/apt/trusted.gpg.d`, eventually `apt-key` will be called, which will make a call to `awk`. On debian that is installed as
```
lrwxrwxrwx 1 65534 65534 21 Jun 17  2022 /usr/bin/awk -> /etc/alternatives/awk
```
Since `/etc/alternatives` is not mounted, the call to awk fails and thus apt thinks there was no valid key found.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mkosi/bubblewrap.py`
Content:
```
1 # SPDX-License-Identifier: LGPL-2.1+
2 import contextlib
3 import enum
4 import logging
5 import os
6 import subprocess
7 import sys
8 from collections.abc import Mapping, Sequence
9 from pathlib import Path
10 from typing import Optional
11 
12 from mkosi.log import ARG_DEBUG_SHELL
13 from mkosi.mounts import finalize_passwd_mounts, mount_overlay
14 from mkosi.run import find_binary, log_process_failure, run
15 from mkosi.state import MkosiState
16 from mkosi.types import _FILE, CompletedProcess, PathString
17 from mkosi.util import flatten, one_zero
18 
19 
20 # https://github.com/torvalds/linux/blob/master/include/uapi/linux/capability.h
21 class Capability(enum.Enum):
22     CAP_NET_ADMIN = 12
23 
24 
25 def have_effective_cap(capability: Capability) -> bool:
26     for line in Path("/proc/self/status").read_text().splitlines():
27         if line.startswith("CapEff:"):
28             hexcap = line.removeprefix("CapEff:").strip()
29             break
30     else:
31         logging.warning(f"\"CapEff:\" not found in /proc/self/status, assuming we don't have {capability}")
32         return False
33 
34     return (int(hexcap, 16) & (1 << capability.value)) != 0
35 
36 
37 def finalize_mounts(state: MkosiState) -> list[str]:
38     mounts = [
39         ((state.config.tools_tree or Path("/")) / subdir, Path("/") / subdir, True)
40         for subdir in (
41             Path("etc/pki"),
42             Path("etc/ssl"),
43             Path("etc/crypto-policies"),
44             Path("etc/ca-certificates"),
45             Path("etc/pacman.d/gnupg"),
46             Path("var/lib/ca-certificates"),
47         )
48         if ((state.config.tools_tree or Path("/")) / subdir).exists()
49     ]
50 
51     mounts += [
52         (d, d, False)
53         for d in (state.workspace, state.config.cache_dir, state.config.output_dir, state.config.build_dir)
54         if d
55     ]
56 
57     mounts += [(d, d, True) for d in state.config.extra_search_paths]
58 
59     return flatten(
60         ["--ro-bind" if readonly else "--bind", os.fspath(src), os.fspath(target)]
61         for src, target, readonly
62         in sorted(set(mounts), key=lambda s: s[1])
63     )
64 
65 
66 def bwrap(
67     state: MkosiState,
68     cmd: Sequence[PathString],
69     *,
70     network: bool = False,
71     devices: bool = False,
72     options: Sequence[PathString] = (),
73     log: bool = True,
74     scripts: Optional[Path] = None,
75     env: Mapping[str, str] = {},
76     stdin: _FILE = None,
77     stdout: _FILE = None,
78     stderr: _FILE = None,
79     input: Optional[str] = None,
80     check: bool = True,
81 ) -> CompletedProcess:
82     cmdline: list[PathString] = [
83         "bwrap",
84         "--ro-bind", "/usr", "/usr",
85         "--ro-bind-try", "/nix/store", "/nix/store",
86         # This mount is writable so bwrap can create extra directories or symlinks inside of it as needed. This isn't a
87         # problem as the package manager directory is created by mkosi and thrown away when the build finishes.
88         "--bind", state.pkgmngr / "etc", "/etc",
89         "--bind", "/var/tmp", "/var/tmp",
90         "--bind", "/tmp", "/tmp",
91         "--bind", Path.cwd(), Path.cwd(),
92         "--chdir", Path.cwd(),
93         "--unshare-pid",
94         "--unshare-ipc",
95         "--unshare-cgroup",
96         *(["--unshare-net"] if not network and have_effective_cap(Capability.CAP_NET_ADMIN) else []),
97         "--die-with-parent",
98         "--proc", "/proc",
99         "--setenv", "SYSTEMD_OFFLINE", one_zero(network),
100     ]
101 
102     if devices:
103         cmdline += [
104             "--bind", "/sys", "/sys",
105             "--dev-bind", "/dev", "/dev",
106         ]
107     else:
108         cmdline += ["--dev", "/dev"]
109 
110     for p in Path("/").iterdir():
111         if p.is_symlink():
112             cmdline += ["--symlink", p.readlink(), p]
113 
114     if network:
115         cmdline += ["--bind", "/etc/resolv.conf", "/etc/resolv.conf"]
116 
117     cmdline += finalize_mounts(state) + [
118         "--setenv", "PATH", f"{scripts or ''}:{os.environ['PATH']}",
119         *options,
120         "sh", "-c", "chmod 1777 /dev/shm && exec $0 \"$@\"",
121     ]
122 
123     if setpgid := find_binary("setpgid"):
124         cmdline += [setpgid, "--foreground", "--"]
125 
126     try:
127         with (
128             mount_overlay([Path("/usr"), state.pkgmngr / "usr"], where=Path("/usr"), lazy=True)
129             if (state.pkgmngr / "usr").exists()
130             else contextlib.nullcontext()
131         ):
132             return run(
133                 [*cmdline, *cmd],
134                 env=env,
135                 log=False,
136                 stdin=stdin,
137                 stdout=stdout,
138                 stderr=stderr,
139                 input=input,
140                 check=check,
141             )
142     except subprocess.CalledProcessError as e:
143         if log:
144             log_process_failure([os.fspath(s) for s in cmd], e.returncode)
145         if ARG_DEBUG_SHELL.get():
146             run([*cmdline, "sh"], stdin=sys.stdin, check=False, env=env, log=False)
147         raise e
148 
149 
150 def apivfs_cmd(root: Path) -> list[PathString]:
151     cmdline: list[PathString] = [
152         "bwrap",
153         "--dev-bind", "/", "/",
154         "--chdir", Path.cwd(),
155         "--tmpfs", root / "run",
156         "--tmpfs", root / "tmp",
157         "--bind", os.getenv("TMPDIR", "/var/tmp"), root / "var/tmp",
158         "--proc", root / "proc",
159         "--dev", root / "dev",
160         # APIVFS generally means chrooting is going to happen so unset TMPDIR just to be safe.
161         "--unsetenv", "TMPDIR",
162     ]
163 
164     if (root / "etc/machine-id").exists():
165         # Make sure /etc/machine-id is not overwritten by any package manager post install scripts.
166         cmdline += ["--ro-bind", root / "etc/machine-id", root / "etc/machine-id"]
167 
168     cmdline += finalize_passwd_mounts(root)
169 
170     if setpgid := find_binary("setpgid"):
171         cmdline += [setpgid, "--foreground", "--"]
172 
173     chmod = f"chmod 1777 {root / 'tmp'} {root / 'var/tmp'} {root / 'dev/shm'}"
174     # Make sure anything running in the root directory thinks it's in a container. $container can't always be
175     # accessed so we write /run/host/container-manager as well which is always accessible.
176     container = f"mkdir {root}/run/host && echo mkosi >{root}/run/host/container-manager"
177 
178     cmdline += ["sh", "-c", f"{chmod} && {container} && exec $0 \"$@\""]
179 
180     return cmdline
181 
182 
183 def chroot_cmd(root: Path, *, resolve: bool = False, options: Sequence[PathString] = ()) -> list[PathString]:
184     cmdline: list[PathString] = [
185         "sh", "-c",
186         # No exec here because we need to clean up the /work directory afterwards.
187         f"trap 'rm -rf {root / 'work'}' EXIT && mkdir -p {root / 'work'} && chown 777 {root / 'work'} && $0 \"$@\"",
188         "bwrap",
189         "--dev-bind", root, "/",
190         "--setenv", "container", "mkosi",
191         "--setenv", "HOME", "/",
192         "--setenv", "PATH", "/work/scripts:/usr/bin:/usr/sbin",
193     ]
194 
195     if resolve:
196         p = Path("etc/resolv.conf")
197         if (root / p).is_symlink():
198             # For each component in the target path, bubblewrap will try to create it if it doesn't exist
199             # yet. If a component in the path is a dangling symlink, bubblewrap will end up calling
200             # mkdir(symlink) which obviously fails if multiple components of the dangling symlink path don't
201             # exist yet. As a workaround, we resolve the symlink ourselves so that bubblewrap will correctly
202             # create all missing components in the target path.
203             p = p.parent / (root / p).readlink()
204 
205         cmdline += ["--ro-bind", "/etc/resolv.conf", Path("/") / p]
206 
207     cmdline += [*options]
208 
209     if setpgid := find_binary("setpgid", root=root):
210         cmdline += [setpgid, "--foreground", "--"]
211 
212     return apivfs_cmd(root) + cmdline
213
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mkosi/bubblewrap.py b/mkosi/bubblewrap.py
--- a/mkosi/bubblewrap.py
+++ b/mkosi/bubblewrap.py
@@ -86,6 +86,7 @@
         # This mount is writable so bwrap can create extra directories or symlinks inside of it as needed. This isn't a
         # problem as the package manager directory is created by mkosi and thrown away when the build finishes.
         "--bind", state.pkgmngr / "etc", "/etc",
+        "--ro-bind-try", "/etc/alternatives", "/etc/alternatives",
         "--bind", "/var/tmp", "/var/tmp",
         "--bind", "/tmp", "/tmp",
         "--bind", Path.cwd(), Path.cwd(),

{"golden_diff": "diff --git a/mkosi/bubblewrap.py b/mkosi/bubblewrap.py\n--- a/mkosi/bubblewrap.py\n+++ b/mkosi/bubblewrap.py\n@@ -86,6 +86,7 @@\n         # This mount is writable so bwrap can create extra directories or symlinks inside of it as needed. This isn't a\n         # problem as the package manager directory is created by mkosi and thrown away when the build finishes.\n         \"--bind\", state.pkgmngr / \"etc\", \"/etc\",\n+        \"--ro-bind-try\", \"/etc/alternatives\", \"/etc/alternatives\",\n         \"--bind\", \"/var/tmp\", \"/var/tmp\",\n         \"--bind\", \"/tmp\", \"/tmp\",\n         \"--bind\", Path.cwd(), Path.cwd(),\n", "issue": "Debian tools which use /etc/alternatives break\nRegression caused by https://github.com/systemd/mkosi/pull/2201, already reported in https://github.com/systemd/mkosi/issues/2206.\r\n\r\nWhen putting a keyring in `/etc/apt/trusted.gpg.d`, eventually `apt-key` will be called, which will make a call to `awk`. On debian that is installed as\r\n```\r\nlrwxrwxrwx 1 65534 65534 21 Jun 17  2022 /usr/bin/awk -> /etc/alternatives/awk\r\n```\r\nSince `/etc/alternatives` is not mounted, the call to awk fails and thus apt thinks there was no valid key found.\n", "before_files": [{"content": "# SPDX-License-Identifier: LGPL-2.1+\nimport contextlib\nimport enum\nimport logging\nimport os\nimport subprocess\nimport sys\nfrom collections.abc import Mapping, Sequence\nfrom pathlib import Path\nfrom typing import Optional\n\nfrom mkosi.log import ARG_DEBUG_SHELL\nfrom mkosi.mounts import finalize_passwd_mounts, mount_overlay\nfrom mkosi.run import find_binary, log_process_failure, run\nfrom mkosi.state import MkosiState\nfrom mkosi.types import _FILE, CompletedProcess, PathString\nfrom mkosi.util import flatten, one_zero\n\n\n# https://github.com/torvalds/linux/blob/master/include/uapi/linux/capability.h\nclass Capability(enum.Enum):\n    CAP_NET_ADMIN = 12\n\n\ndef have_effective_cap(capability: Capability) -> bool:\n    for line in Path(\"/proc/self/status\").read_text().splitlines():\n        if line.startswith(\"CapEff:\"):\n            hexcap = line.removeprefix(\"CapEff:\").strip()\n            break\n    else:\n        logging.warning(f\"\\\"CapEff:\\\" not found in /proc/self/status, assuming we don't have {capability}\")\n        return False\n\n    return (int(hexcap, 16) & (1 << capability.value)) != 0\n\n\ndef finalize_mounts(state: MkosiState) -> list[str]:\n    mounts = [\n        ((state.config.tools_tree or Path(\"/\")) / subdir, Path(\"/\") / subdir, True)\n        for subdir in (\n            Path(\"etc/pki\"),\n            Path(\"etc/ssl\"),\n            Path(\"etc/crypto-policies\"),\n            Path(\"etc/ca-certificates\"),\n            Path(\"etc/pacman.d/gnupg\"),\n            Path(\"var/lib/ca-certificates\"),\n        )\n        if ((state.config.tools_tree or Path(\"/\")) / subdir).exists()\n    ]\n\n    mounts += [\n        (d, d, False)\n        for d in (state.workspace, state.config.cache_dir, state.config.output_dir, state.config.build_dir)\n        if d\n    ]\n\n    mounts += [(d, d, True) for d in state.config.extra_search_paths]\n\n    return flatten(\n        [\"--ro-bind\" if readonly else \"--bind\", os.fspath(src), os.fspath(target)]\n        for src, target, readonly\n        in sorted(set(mounts), key=lambda s: s[1])\n    )\n\n\ndef bwrap(\n    state: MkosiState,\n    cmd: Sequence[PathString],\n    *,\n    network: bool = False,\n    devices: bool = False,\n    options: Sequence[PathString] = (),\n    log: bool = True,\n    scripts: Optional[Path] = None,\n    env: Mapping[str, str] = {},\n    stdin: _FILE = None,\n    stdout: _FILE = None,\n    stderr: _FILE = None,\n    input: Optional[str] = None,\n    check: bool = True,\n) -> CompletedProcess:\n    cmdline: list[PathString] = [\n        \"bwrap\",\n        \"--ro-bind\", \"/usr\", \"/usr\",\n        \"--ro-bind-try\", \"/nix/store\", \"/nix/store\",\n        # This mount is writable so bwrap can create extra directories or symlinks inside of it as needed. This isn't a\n        # problem as the package manager directory is created by mkosi and thrown away when the build finishes.\n        \"--bind\", state.pkgmngr / \"etc\", \"/etc\",\n        \"--bind\", \"/var/tmp\", \"/var/tmp\",\n        \"--bind\", \"/tmp\", \"/tmp\",\n        \"--bind\", Path.cwd(), Path.cwd(),\n        \"--chdir\", Path.cwd(),\n        \"--unshare-pid\",\n        \"--unshare-ipc\",\n        \"--unshare-cgroup\",\n        *([\"--unshare-net\"] if not network and have_effective_cap(Capability.CAP_NET_ADMIN) else []),\n        \"--die-with-parent\",\n        \"--proc\", \"/proc\",\n        \"--setenv\", \"SYSTEMD_OFFLINE\", one_zero(network),\n    ]\n\n    if devices:\n        cmdline += [\n            \"--bind\", \"/sys\", \"/sys\",\n            \"--dev-bind\", \"/dev\", \"/dev\",\n        ]\n    else:\n        cmdline += [\"--dev\", \"/dev\"]\n\n    for p in Path(\"/\").iterdir():\n        if p.is_symlink():\n            cmdline += [\"--symlink\", p.readlink(), p]\n\n    if network:\n        cmdline += [\"--bind\", \"/etc/resolv.conf\", \"/etc/resolv.conf\"]\n\n    cmdline += finalize_mounts(state) + [\n        \"--setenv\", \"PATH\", f\"{scripts or ''}:{os.environ['PATH']}\",\n        *options,\n        \"sh\", \"-c\", \"chmod 1777 /dev/shm && exec $0 \\\"$@\\\"\",\n    ]\n\n    if setpgid := find_binary(\"setpgid\"):\n        cmdline += [setpgid, \"--foreground\", \"--\"]\n\n    try:\n        with (\n            mount_overlay([Path(\"/usr\"), state.pkgmngr / \"usr\"], where=Path(\"/usr\"), lazy=True)\n            if (state.pkgmngr / \"usr\").exists()\n            else contextlib.nullcontext()\n        ):\n            return run(\n                [*cmdline, *cmd],\n                env=env,\n                log=False,\n                stdin=stdin,\n                stdout=stdout,\n                stderr=stderr,\n                input=input,\n                check=check,\n            )\n    except subprocess.CalledProcessError as e:\n        if log:\n            log_process_failure([os.fspath(s) for s in cmd], e.returncode)\n        if ARG_DEBUG_SHELL.get():\n            run([*cmdline, \"sh\"], stdin=sys.stdin, check=False, env=env, log=False)\n        raise e\n\n\ndef apivfs_cmd(root: Path) -> list[PathString]:\n    cmdline: list[PathString] = [\n        \"bwrap\",\n        \"--dev-bind\", \"/\", \"/\",\n        \"--chdir\", Path.cwd(),\n        \"--tmpfs\", root / \"run\",\n        \"--tmpfs\", root / \"tmp\",\n        \"--bind\", os.getenv(\"TMPDIR\", \"/var/tmp\"), root / \"var/tmp\",\n        \"--proc\", root / \"proc\",\n        \"--dev\", root / \"dev\",\n        # APIVFS generally means chrooting is going to happen so unset TMPDIR just to be safe.\n        \"--unsetenv\", \"TMPDIR\",\n    ]\n\n    if (root / \"etc/machine-id\").exists():\n        # Make sure /etc/machine-id is not overwritten by any package manager post install scripts.\n        cmdline += [\"--ro-bind\", root / \"etc/machine-id\", root / \"etc/machine-id\"]\n\n    cmdline += finalize_passwd_mounts(root)\n\n    if setpgid := find_binary(\"setpgid\"):\n        cmdline += [setpgid, \"--foreground\", \"--\"]\n\n    chmod = f\"chmod 1777 {root / 'tmp'} {root / 'var/tmp'} {root / 'dev/shm'}\"\n    # Make sure anything running in the root directory thinks it's in a container. $container can't always be\n    # accessed so we write /run/host/container-manager as well which is always accessible.\n    container = f\"mkdir {root}/run/host && echo mkosi >{root}/run/host/container-manager\"\n\n    cmdline += [\"sh\", \"-c\", f\"{chmod} && {container} && exec $0 \\\"$@\\\"\"]\n\n    return cmdline\n\n\ndef chroot_cmd(root: Path, *, resolve: bool = False, options: Sequence[PathString] = ()) -> list[PathString]:\n    cmdline: list[PathString] = [\n        \"sh\", \"-c\",\n        # No exec here because we need to clean up the /work directory afterwards.\n        f\"trap 'rm -rf {root / 'work'}' EXIT && mkdir -p {root / 'work'} && chown 777 {root / 'work'} && $0 \\\"$@\\\"\",\n        \"bwrap\",\n        \"--dev-bind\", root, \"/\",\n        \"--setenv\", \"container\", \"mkosi\",\n        \"--setenv\", \"HOME\", \"/\",\n        \"--setenv\", \"PATH\", \"/work/scripts:/usr/bin:/usr/sbin\",\n    ]\n\n    if resolve:\n        p = Path(\"etc/resolv.conf\")\n        if (root / p).is_symlink():\n            # For each component in the target path, bubblewrap will try to create it if it doesn't exist\n            # yet. If a component in the path is a dangling symlink, bubblewrap will end up calling\n            # mkdir(symlink) which obviously fails if multiple components of the dangling symlink path don't\n            # exist yet. As a workaround, we resolve the symlink ourselves so that bubblewrap will correctly\n            # create all missing components in the target path.\n            p = p.parent / (root / p).readlink()\n\n        cmdline += [\"--ro-bind\", \"/etc/resolv.conf\", Path(\"/\") / p]\n\n    cmdline += [*options]\n\n    if setpgid := find_binary(\"setpgid\", root=root):\n        cmdline += [setpgid, \"--foreground\", \"--\"]\n\n    return apivfs_cmd(root) + cmdline\n", "path": "mkosi/bubblewrap.py"}], "after_files": [{"content": "# SPDX-License-Identifier: LGPL-2.1+\nimport contextlib\nimport enum\nimport logging\nimport os\nimport subprocess\nimport sys\nfrom collections.abc import Mapping, Sequence\nfrom pathlib import Path\nfrom typing import Optional\n\nfrom mkosi.log import ARG_DEBUG_SHELL\nfrom mkosi.mounts import finalize_passwd_mounts, mount_overlay\nfrom mkosi.run import find_binary, log_process_failure, run\nfrom mkosi.state import MkosiState\nfrom mkosi.types import _FILE, CompletedProcess, PathString\nfrom mkosi.util import flatten, one_zero\n\n\n# https://github.com/torvalds/linux/blob/master/include/uapi/linux/capability.h\nclass Capability(enum.Enum):\n    CAP_NET_ADMIN = 12\n\n\ndef have_effective_cap(capability: Capability) -> bool:\n    for line in Path(\"/proc/self/status\").read_text().splitlines():\n        if line.startswith(\"CapEff:\"):\n            hexcap = line.removeprefix(\"CapEff:\").strip()\n            break\n    else:\n        logging.warning(f\"\\\"CapEff:\\\" not found in /proc/self/status, assuming we don't have {capability}\")\n        return False\n\n    return (int(hexcap, 16) & (1 << capability.value)) != 0\n\n\ndef finalize_mounts(state: MkosiState) -> list[str]:\n    mounts = [\n        ((state.config.tools_tree or Path(\"/\")) / subdir, Path(\"/\") / subdir, True)\n        for subdir in (\n            Path(\"etc/pki\"),\n            Path(\"etc/ssl\"),\n            Path(\"etc/crypto-policies\"),\n            Path(\"etc/ca-certificates\"),\n            Path(\"etc/pacman.d/gnupg\"),\n            Path(\"var/lib/ca-certificates\"),\n        )\n        if ((state.config.tools_tree or Path(\"/\")) / subdir).exists()\n    ]\n\n    mounts += [\n        (d, d, False)\n        for d in (state.workspace, state.config.cache_dir, state.config.output_dir, state.config.build_dir)\n        if d\n    ]\n\n    mounts += [(d, d, True) for d in state.config.extra_search_paths]\n\n    return flatten(\n        [\"--ro-bind\" if readonly else \"--bind\", os.fspath(src), os.fspath(target)]\n        for src, target, readonly\n        in sorted(set(mounts), key=lambda s: s[1])\n    )\n\n\ndef bwrap(\n    state: MkosiState,\n    cmd: Sequence[PathString],\n    *,\n    network: bool = False,\n    devices: bool = False,\n    options: Sequence[PathString] = (),\n    log: bool = True,\n    scripts: Optional[Path] = None,\n    env: Mapping[str, str] = {},\n    stdin: _FILE = None,\n    stdout: _FILE = None,\n    stderr: _FILE = None,\n    input: Optional[str] = None,\n    check: bool = True,\n) -> CompletedProcess:\n    cmdline: list[PathString] = [\n        \"bwrap\",\n        \"--ro-bind\", \"/usr\", \"/usr\",\n        \"--ro-bind-try\", \"/nix/store\", \"/nix/store\",\n        # This mount is writable so bwrap can create extra directories or symlinks inside of it as needed. This isn't a\n        # problem as the package manager directory is created by mkosi and thrown away when the build finishes.\n        \"--bind\", state.pkgmngr / \"etc\", \"/etc\",\n        \"--ro-bind-try\", \"/etc/alternatives\", \"/etc/alternatives\",\n        \"--bind\", \"/var/tmp\", \"/var/tmp\",\n        \"--bind\", \"/tmp\", \"/tmp\",\n        \"--bind\", Path.cwd(), Path.cwd(),\n        \"--chdir\", Path.cwd(),\n        \"--unshare-pid\",\n        \"--unshare-ipc\",\n        \"--unshare-cgroup\",\n        *([\"--unshare-net\"] if not network and have_effective_cap(Capability.CAP_NET_ADMIN) else []),\n        \"--die-with-parent\",\n        \"--proc\", \"/proc\",\n        \"--setenv\", \"SYSTEMD_OFFLINE\", one_zero(network),\n    ]\n\n    if devices:\n        cmdline += [\n            \"--bind\", \"/sys\", \"/sys\",\n            \"--dev-bind\", \"/dev\", \"/dev\",\n        ]\n    else:\n        cmdline += [\"--dev\", \"/dev\"]\n\n    for p in Path(\"/\").iterdir():\n        if p.is_symlink():\n            cmdline += [\"--symlink\", p.readlink(), p]\n\n    if network:\n        cmdline += [\"--bind\", \"/etc/resolv.conf\", \"/etc/resolv.conf\"]\n\n    cmdline += finalize_mounts(state) + [\n        \"--setenv\", \"PATH\", f\"{scripts or ''}:{os.environ['PATH']}\",\n        *options,\n        \"sh\", \"-c\", \"chmod 1777 /dev/shm && exec $0 \\\"$@\\\"\",\n    ]\n\n    if setpgid := find_binary(\"setpgid\"):\n        cmdline += [setpgid, \"--foreground\", \"--\"]\n\n    try:\n        with (\n            mount_overlay([Path(\"/usr\"), state.pkgmngr / \"usr\"], where=Path(\"/usr\"), lazy=True)\n            if (state.pkgmngr / \"usr\").exists()\n            else contextlib.nullcontext()\n        ):\n            return run(\n                [*cmdline, *cmd],\n                env=env,\n                log=False,\n                stdin=stdin,\n                stdout=stdout,\n                stderr=stderr,\n                input=input,\n                check=check,\n            )\n    except subprocess.CalledProcessError as e:\n        if log:\n            log_process_failure([os.fspath(s) for s in cmd], e.returncode)\n        if ARG_DEBUG_SHELL.get():\n            run([*cmdline, \"sh\"], stdin=sys.stdin, check=False, env=env, log=False)\n        raise e\n\n\ndef apivfs_cmd(root: Path) -> list[PathString]:\n    cmdline: list[PathString] = [\n        \"bwrap\",\n        \"--dev-bind\", \"/\", \"/\",\n        \"--chdir\", Path.cwd(),\n        \"--tmpfs\", root / \"run\",\n        \"--tmpfs\", root / \"tmp\",\n        \"--bind\", os.getenv(\"TMPDIR\", \"/var/tmp\"), root / \"var/tmp\",\n        \"--proc\", root / \"proc\",\n        \"--dev\", root / \"dev\",\n        # APIVFS generally means chrooting is going to happen so unset TMPDIR just to be safe.\n        \"--unsetenv\", \"TMPDIR\",\n    ]\n\n    if (root / \"etc/machine-id\").exists():\n        # Make sure /etc/machine-id is not overwritten by any package manager post install scripts.\n        cmdline += [\"--ro-bind\", root / \"etc/machine-id\", root / \"etc/machine-id\"]\n\n    cmdline += finalize_passwd_mounts(root)\n\n    if setpgid := find_binary(\"setpgid\"):\n        cmdline += [setpgid, \"--foreground\", \"--\"]\n\n    chmod = f\"chmod 1777 {root / 'tmp'} {root / 'var/tmp'} {root / 'dev/shm'}\"\n    # Make sure anything running in the root directory thinks it's in a container. $container can't always be\n    # accessed so we write /run/host/container-manager as well which is always accessible.\n    container = f\"mkdir {root}/run/host && echo mkosi >{root}/run/host/container-manager\"\n\n    cmdline += [\"sh\", \"-c\", f\"{chmod} && {container} && exec $0 \\\"$@\\\"\"]\n\n    return cmdline\n\n\ndef chroot_cmd(root: Path, *, resolve: bool = False, options: Sequence[PathString] = ()) -> list[PathString]:\n    cmdline: list[PathString] = [\n        \"sh\", \"-c\",\n        # No exec here because we need to clean up the /work directory afterwards.\n        f\"trap 'rm -rf {root / 'work'}' EXIT && mkdir -p {root / 'work'} && chown 777 {root / 'work'} && $0 \\\"$@\\\"\",\n        \"bwrap\",\n        \"--dev-bind\", root, \"/\",\n        \"--setenv\", \"container\", \"mkosi\",\n        \"--setenv\", \"HOME\", \"/\",\n        \"--setenv\", \"PATH\", \"/work/scripts:/usr/bin:/usr/sbin\",\n    ]\n\n    if resolve:\n        p = Path(\"etc/resolv.conf\")\n        if (root / p).is_symlink():\n            # For each component in the target path, bubblewrap will try to create it if it doesn't exist\n            # yet. If a component in the path is a dangling symlink, bubblewrap will end up calling\n            # mkdir(symlink) which obviously fails if multiple components of the dangling symlink path don't\n            # exist yet. As a workaround, we resolve the symlink ourselves so that bubblewrap will correctly\n            # create all missing components in the target path.\n            p = p.parent / (root / p).readlink()\n\n        cmdline += [\"--ro-bind\", \"/etc/resolv.conf\", Path(\"/\") / p]\n\n    cmdline += [*options]\n\n    if setpgid := find_binary(\"setpgid\", root=root):\n        cmdline += [setpgid, \"--foreground\", \"--\"]\n\n    return apivfs_cmd(root) + cmdline\n", "path": "mkosi/bubblewrap.py"}]}