import random
import resource
import shutil
-import signal
import socket
import struct
import subprocess
format_bytes,
swtpm_setup_version,
systemd_pty_forward,
- systemd_tool_version,
want_selinux_relabel,
yes_no,
)
cmdline,
pass_fds=(sock.fileno(),),
sandbox=config.sandbox(options=["--bind", state, workdir(Path(state))]),
- setup=scope_cmd(
- name=f"mkosi-swtpm-{config.machine_or_name()}",
- description=f"swtpm for {config.machine_or_name()}",
- ),
) as proc:
yield path
proc.terminate()
directory: Path,
*,
uidmap: bool = True,
- name: Optional[str] = None,
selinux: bool = False,
) -> Iterator[Path]:
virtiofsd = find_virtiofsd(root=config.tools(), extra=config.extra_search_paths)
# We want RuntimeBuildSources= and RuntimeTrees= to do the right thing even when running mkosi vm
# as root without the source directories necessarily being owned by root. We achieve this by running
# virtiofsd as the owner of the source directory and then mapping that uid to root.
- if not name:
- name = f"{config.machine_or_name()}-{systemd_escape(config, directory, path=True)}"
- else:
- name = systemd_escape(config, name)
-
- name = f"mkosi-virtiofsd-{name}"
- description = f"virtiofsd for machine {config.machine_or_name()} for {directory}"
- scope = []
- if st:
- scope = scope_cmd(name=name, description=description, user=st.st_uid, group=st.st_gid)
- elif not uidmap and (os.getuid() == 0 or unshare_version() >= "2.38"):
- scope = scope_cmd(name=name, description=description)
with spawn(
cmdline,
pass_fds=(sock.fileno(),),
- user=st.st_uid if st and not scope else None,
- group=st.st_gid if st and not scope else None,
- # If we're booting from virtiofs and unshare is too old, we don't set up a scope so we can use
- # our own function to become root in the subuid range.
+ user=st.st_uid if st else None,
+ group=st.st_gid if st else None,
+ # If we're booting from virtiofs and unshare is too old, we use our own function to become root
+ # in the subuid range if needed.
# TODO: Drop this as soon as we drop CentOS Stream 9 support and can rely on newer unshare
# features.
- preexec=become_root_in_subuid_range if not scope and not uidmap else None,
+ preexec=(
+ become_root_in_subuid_range
+ if not uidmap and os.getuid() != 0 and unshare_version() < "2.38"
+ else None
+ ),
sandbox=config.sandbox(
options=[
"--bind", directory, workdir(directory),
*(["--become-root"] if uidmap else []),
],
),
- setup=scope + (become_root_in_subuid_range_cmd() if scope and not uidmap else []),
+ setup=(
+ become_root_in_subuid_range_cmd()
+ if not uidmap and os.getuid() != 0 and unshare_version() >= "2.38"
+ else []
+ ),
) as proc: # fmt: skip
yield path
proc.terminate()
user = d.stat().st_uid if os.getuid() == 0 else None
group = d.stat().st_gid if os.getuid() == 0 else None
- scope = scope_cmd(
- name=f"mkosi-journal-remote-{config.machine_or_name()}",
- description=f"mkosi systemd-journal-remote for {config.machine_or_name()}",
- user=user,
- group=group,
- )
with spawn(
[
"--pack-fds",
],
),
- setup=scope,
- user=user if not scope else None,
- group=group if not scope else None,
+ user=user,
+ group=group,
) as proc: # fmt: skip
yield
proc.terminate()
return d
-def scope_cmd(
- name: str,
- description: str,
- user: Optional[int] = None,
- group: Optional[int] = None,
- properties: Sequence[str] = (),
- environment: bool = True,
-) -> list[str]:
- if not find_binary("systemd-run"):
- return []
-
- if os.getuid() != 0 and "DBUS_SESSION_BUS_ADDRESS" in os.environ and "XDG_RUNTIME_DIR" in os.environ:
- env = {
- "DBUS_SESSION_BUS_ADDRESS": os.environ["DBUS_SESSION_BUS_ADDRESS"],
- "XDG_RUNTIME_DIR": os.environ["XDG_RUNTIME_DIR"],
- }
- elif os.getuid() == 0:
- if "DBUS_SYSTEM_ADDRESS" in os.environ:
- env = {"DBUS_SYSTEM_ADDRESS": os.environ["DBUS_SYSTEM_ADDRESS"]}
- elif Path("/run/dbus/system_bus_socket").exists():
- env = {"DBUS_SYSTEM_ADDRESS": "/run/dbus/system_bus_socket"}
- else:
- return []
- else:
- return []
-
- return [
- "env",
- *(f"{k}={v}" for k, v in env.items() if environment),
- "systemd-run",
- "--system" if os.getuid() == 0 else "--user",
- *(["--quiet"] if not ARG_DEBUG.get() else []),
- "--unit", name,
- "--description", description,
- "--scope",
- "--collect",
- *(["--expand-environment=no"] if systemd_tool_version("systemd-run") >= 254 else []),
- *(["--uid", str(user)] if user is not None else []),
- *(["--gid", str(group)] if group is not None else []),
- *([f"--property={p}" for p in properties]),
- ] # fmt: skip
-
-
def finalize_register(config: Config) -> bool:
if config.register == ConfigFeature.disabled:
return False
**({"vSockCid": cid} if cid is not None else {}),
**({"sshAddress": f"vsock/{cid}"} if cid is not None else {}),
**({"sshPrivateKeyPath": f"{config.ssh_key}"} if config.ssh_key else {}),
+ **({"allocateUnit": True}),
}
),
],
start_virtiofsd(
config,
fname,
- name=config.machine_or_name(),
uidmap=False,
selinux=bool(want_selinux_relabel(config, fname, fatal=False)),
),
network=True,
devices=True,
relaxed=True,
- options=["--same-dir", "--suspend"],
- ),
- setup=scope_cmd(
- name=name,
- description=f"mkosi Virtual Machine {name}",
- properties=config.unit_properties,
- environment=False,
+ options=["--same-dir"],
),
) as proc:
# We have to close these before we wait for qemu otherwise we'll deadlock as qemu will never
for fd in qemu_device_fds.values():
os.close(fd)
- os.waitid(os.P_PID, proc.pid, os.WEXITED | os.WSTOPPED | os.WNOWAIT)
-
register_machine(config, proc.pid, fname, cid)
# Fork and threads don't mix well when using preexec functions, so start the thread to handle
AsyncioThread(functools.partial(vsock_notify_handler, sock=vsock))
)
- proc.send_signal(signal.SIGCONT)
-
if notify and (status := int({k: v for k, v in notify.process()}.get("EXIT_STATUS", "0"))) != 0:
raise subprocess.CalledProcessError(status, cmdline)