Compare commits

...

62 Commits

Author SHA1 Message Date
Gaétan Lepage
575fbaf778 ty: 0.0.95 -> 0.0.96 (#582783) 2026-11-04 11:38:00 +00:00
nixpkgs-ci[bot]
1b0661cb8e Merge master into staging-nixos 2026-11-04 06:11:11 +00:00
Ihar Hrachyshka
a43b7eab39 nixos/tests/x11: add -noreset flag to nspawn X11 tests (#582707) 2026-11-04 01:43:25 +00:00
nixpkgs-ci[bot]
f43d13a970 Merge master into staging-nixos 2026-11-04 00:16:26 +00:00
Will Fancher
43be3d4cfb nixos/systemd/tpm2: ensure systemd-pcrextend.socket is pulled in (#582268) 2026-11-03 20:52:06 +00:00
Naxdy
abae798b6c nixosTests/systemd-initrd-luks-tpm2: test for ability to measure LUKS volume keys 2026-11-03 21:41:56 +01:00
Naxdy
cf9a0df709 nixos/systemd/tpm2: ensure systemd-pcrextend.socket is pulled in
Assisted-By: omp/DeepSeek-V4.1-Flash-1017
2026-11-03 21:41:56 +01:00
Jörg Thalheim
4d7096f6c6 nixos-firewall-tool: run shellcheck as a passthru test (#582676) 2026-11-03 19:45:13 +00:00
Jörg Thalheim
fadbd12b88 nixos-firewall-tool: run shellcheck as a passthru test
Checking whether shellcheck can be built forces the whole Haskell
package set while evaluating any NixOS system with a firewall. The
check now runs in passthru.tests instead.
2026-11-03 20:33:30 +01:00
Jost Alemann
12214b623a ty: 0.0.95 -> 0.0.96
Changelog: https://github.com/astral-sh/ty/releases/tag/0.0.96
Diff: https://github.com/astral-sh/ty/compare/0.0.95...0.0.96
2026-11-03 19:32:53 +01:00
nixpkgs-ci[bot]
3328cd18b2 Merge master into staging-nixos 2026-11-03 18:09:45 +00:00
Maximilian Bosch
0fa2336746 nixos/tests/x11: add -noreset flag to nspawn X11 tests
Works around X resetting itself when the only connection (from
wait_for_x()) disconnects[1].

With this change, `nixosTests.nixos-test-driver.nspawn-interactive`
seems way less flaky, I couldn't trigger the hanging test scenario
within 50 runs.

[1] More context: https://github.com/NixOS/nixpkgs/pull/578540#issuecomment-6593586095

Co-authored-by: Ihar Hrachyshka <ihar.hrachyshka@gmail.com>
2026-11-03 15:51:07 +01:00
nixpkgs-ci[bot]
3b574a194d Merge master into staging-nixos 2026-11-03 12:09:08 +00:00
nixpkgs-ci[bot]
573530121c Merge master into staging-nixos 2026-11-03 06:10:58 +00:00
nixpkgs-ci[bot]
7de48641a5 Merge master into staging-nixos 2026-11-03 00:14:51 +00:00
nixpkgs-ci[bot]
0032afe410 Merge master into staging-nixos 2026-11-02 18:08:38 +00:00
Philip Taron
ea33e2134e make-initrd-ng: handle dlopen SONAME alternatives (#578841) 2026-11-02 17:24:12 +00:00
Philip Taron
0835e2960f make-initrd-ng: format with rustfmt
Assisted-by: Claude Code (claude-opus-5-5)
2026-11-02 08:22:08 -08:00
Philip Taron
7732580291 make-initrd-ng: tune is_glibc_runtime
Assisted-by: Claude Code (claude-opus-5-5)
2026-11-02 08:20:11 -08:00
Philip Taron
70cffbcf42 make-initrd-ng: inline find_library
Assisted-by: Claude Code (claude-opus-5-5)
2026-11-02 08:19:56 -08:00
Jacek Galowicz
2c4e5922cf Test driver doctests (#581841) 2026-11-02 16:19:11 +00:00
nixpkgs-ci[bot]
964de0a0cf Merge master into staging-nixos 2026-11-02 13:55:22 +00:00
Ryan Lahfa
f4b42644cb nixos/test-driver: fix VM startup in large tests, port to selectors, improve logging (#578536) 2026-11-02 12:52:34 +00:00
nixpkgs-ci[bot]
2d5423b857 Merge master into staging-nixos 2026-11-02 12:10:31 +00:00
Jacek Galowicz
ad8d8b2dd6 test-driver: outsource some code to pure functions for doctests 2026-11-02 09:28:00 +01:00
Jacek Galowicz
fdc0faecfb test-driver: doctest some pure functions 2026-11-02 09:28:00 +01:00
Maximilian Bosch
addb72b9dc nixos/test-driver: dim log timer (#581943) 2026-11-02 07:56:01 +00:00
zowoq
48b13a3d84 linux: enable strictDeps (#581314) 2026-11-02 07:06:09 +00:00
nixpkgs-ci[bot]
957f263e78 Merge master into staging-nixos 2026-11-02 06:13:58 +00:00
Sandro
e9035336b3 nixos-enter: unset HOSTNAME and NIX_REMOTE to not confuse system or u… (#522097) 2026-11-02 01:05:36 +00:00
nixpkgs-ci[bot]
74801e0478 Merge master into staging-nixos 2026-11-02 00:17:37 +00:00
Maximilian Bosch
045ca2d4ec nixos/test-driver: dim log timer
Since 509e09e6 we have a prefix like `[2.00s]` that shows the elapsed
seconds of a test for each log-message.

While it's arguably useful information, for me it's noise that I only
care about in some specific cases which is why I'd prefer to have this
dimmed as all the serial output.
2026-11-01 22:23:14 +01:00
Yueh-Shun Li
f8e3bf8a2e brltty: add polkit support (#544333) 2026-11-01 19:47:31 +00:00
nixpkgs-ci[bot]
1de0066f4b Merge master into staging-nixos 2026-11-01 18:06:55 +00:00
Jacek Galowicz
7d47ee0895 test-driver: add doctesting in checkphase 2026-11-01 15:13:25 +01:00
nixpkgs-ci[bot]
55a9de48ba Merge master into staging-nixos 2026-11-01 12:08:42 +00:00
nixpkgs-ci[bot]
c3c3cb0a91 Merge master into staging-nixos 2026-11-01 10:06:58 +00:00
nixpkgs-ci[bot]
314856eb73 Merge master into staging-nixos 2026-11-01 06:10:20 +00:00
nixpkgs-ci[bot]
f45f87ad31 Merge master into staging-nixos 2026-11-01 00:19:47 +00:00
Will Fancher
d88a77cd07 nixos/systemd-initrd: add more disk discoverability (#581485) 2026-10-31 18:55:42 +00:00
matthewcroughan
4f5a4f6b95 nixos/systemd-initrd: add DDI tooling and systemd-loop@.service
This allows for more disk discoverability by using the "DDI"
functionality of systemd
2026-10-31 18:24:34 +00:00
matthewcroughan
5958536127 nixos/systemd: add systemd-loop@.service 2026-10-31 18:16:29 +00:00
nixpkgs-ci[bot]
6f5ee22df1 Merge master into staging-nixos 2026-10-31 18:07:08 +00:00
gabby
16c8316a19 nixos/fontconfig: Make Fontconfig package configurable (#580555) 2026-10-31 16:16:00 +00:00
nixpkgs-ci[bot]
17e20319af Merge master into staging-nixos 2026-10-31 12:08:16 +00:00
eljamm
4f64ec7425 linux: enable strictDeps 2026-10-31 10:24:38 +01:00
nixpkgs-ci[bot]
7c24500e5f Merge master into staging-nixos 2026-10-31 06:09:39 +00:00
Sandro
71c3728e60 btrfs-progs: 7.1 -> 7.3 (#579549) 2026-10-31 02:07:46 +00:00
nixpkgs-ci[bot]
bccbc81037 Merge master into staging-nixos 2026-10-31 00:15:33 +00:00
Fabián Heredia Montiel
5586e18a7f libslirp: 4.9.5 -> 4.9.6 (#578803) 2026-10-30 23:20:54 +00:00
nixpkgs-ci[bot]
3ee7fa5291 Merge master into staging-nixos 2026-10-30 20:06:04 +00:00
nixpkgs-ci[bot]
66cf75f595 Merge master into staging-nixos 2026-10-30 18:07:28 +00:00
Victor Engmark
94c56eb596 nixos/fontconfig: Make Fontconfig package configurable
And referrable.
2026-10-29 16:29:23 +01:00
Maximilian Bosch
0d3c836963 nixos/qemu-vm: fix startup issues, port virtiofs job-control to Python
The issue we were chasing was an number of QEMU VMs being started up in
large tests[1], introduced by the migration from 9pfs to virtiofs[2]. What
happened here was

* When having a large number of VMs (and at least 3x the amount of
  virtiofsd procs), some processes aren't ready for operation leading
  QEMU to fail because the sockets it exposes aren't ready yet.

* The tests hang indefinitely now since the process is dead, but the
  stdout fd is still open as it's used by the virtiofsd processes.

* As a result, the startup logic for a VM in the test-driver assumes the
  VM to be dead and tries to fully read its stdout which hangs since it's
  still used by the virtiofsd procs.

I tried to fix this in pure Bash, but decided to give up since Bash's
job control logic is a hot mess. Also, a naive implementation would
notably slow down the startup of tests since we need a loop for
virtiofsd having set up all sockets before starting QEMU and with
asyncio we can wait on all of them rather than doing all of this
sequentially.

The virtiofsd configuration is passed to the Python script via CLI args
on purpose to still evaluate Bash variables that are sadly part of the
interface.

I think it'd make sense to move more of the start script into this
Python package, but my main goal is to have something ready where we can
be confident that the original bug got fixed. This refactoring can be
done iteratively in follow-ups.

[1] e.g. https://hydra.nixos.org/build/350180953/step/3/log/tail
[2] https://github.com/NixOS/nixpkgs/pull/552774
2026-10-29 09:38:50 +01:00
K900
f65c491441 lib/test-driver: port to selectors
This seems to bypass whatever nonsense we've been hitting with select.select,
somehow.
2026-10-29 09:38:48 +01:00
K900
fa391feabe lib/test-driver: also add machine name to the other errors 2026-10-29 09:37:10 +01:00
K900
e5d47c704e lib/test-driver: add a timeout for waiting for QEMU to connect
We still don't know _why_ it's not connecting, but at least
now we will fail (reasonably) fast if it doesn't instead of
hogging a build slot forever.
2026-10-29 09:37:10 +01:00
R. Ryantm
215954bfe9 btrfs-progs: 7.1 -> 7.3 2026-10-27 05:36:12 +00:00
Aliaksandr
45e574d8f1 make-initrd-ng: handle dlopen SONAME alternatives
Assisted-by: codex with gpt-5.6-sol-high
2026-10-25 17:02:24 +02:00
R. Ryantm
97f93996c6 libslirp: 4.9.5 -> 4.9.6 2026-10-25 12:37:38 +00:00
Bram Duvigneau
43e45601c1 brltty: add polkit support
BRLTTY enables Polkit support by default (--enable-polkit), but detects it
solely via `pkg-config --exists polkit-gobject-1`. Since polkit was not in
buildInputs, detection silently failed and BRLTTY was built without it, while
still installing its Polkit policy (org.a11y.brlapi.policy).

Without Polkit support, BrlAPI clients such as Orca cannot authorize against
BRLTTY, so a braille display is unusable from the desktop.

Polkit is Linux-only, so this follows the existing alsaSupport/systemdSupport
pattern and gates on lib.meta.availableOn. The configure flag is passed
explicitly rather than relying on pkg-config detection, so the feature can no
longer be dropped silently.

Fixes #540757

Assisted-by: Claude Code (Claude Opus 4.8, claude-opus-4-8)
2026-09-15 14:05:15 +02:00
Sandro Jäckel
5563d930fa nixos-enter: unset HOSTNAME and NIX_REMOTE to not confuse system or use the ISOs nix daemon
This is necessary when executing nixos-enter from the root user.
2026-05-20 00:58:41 +02:00
28 changed files with 622 additions and 164 deletions

View File

@@ -10,6 +10,7 @@
junit-xml,
ptpython,
pydantic,
pytest,
python,
ovmfvartool,
remote-pdb,
@@ -80,11 +81,14 @@ buildPythonApplication {
doCheck = true;
nativeCheckInputs = [
pytest
ruff
ty
];
checkPhase = ''
echo -e "\x1b[32m## run doctests\x1b[0m"
pytest --doctest-modules -p no:cacheprovider test_driver
echo -e "\x1b[32m## run ty\x1b[0m"
# Installation adds the driver to PYTHONPATH; keep ty imports in the source tree.
PYTHONPATH=".:$PYTHONPATH" ty check --error-on-warning test_driver extract-docstrings.py

View File

@@ -87,6 +87,30 @@ def get_tmp_dir() -> Path:
def pythonize_name(name: str) -> str:
"""Turn a machine name into a valid Python identifier.
Names that already are valid identifiers stay the same:
>>> pythonize_name("node_1")
'node_1'
Every character that is not allowed in an identifier becomes "_":
>>> pythonize_name("my-vm")
'my_vm'
>>> pythonize_name("web.example.org")
'web_example_org'
>>> pythonize_name("münchen")
'm_nchen'
A leading digit is replaced rather than prefixed, so it gets lost, while
digits later in the name are kept:
>>> pythonize_name("1node")
'_node'
>>> pythonize_name("12node")
'_2node'
"""
return re.sub(r"^[^A-Za-z_]|[^A-Za-z0-9_]", "_", name)

View File

@@ -14,6 +14,22 @@ def as_timedelta(duration: Duration) -> dt.timedelta:
Bare numbers are interpreted as seconds. This keeps existing test scripts
that pass plain integers working while `datetime.timedelta` becomes the preferred
way to express durations.
>>> as_timedelta(dt.timedelta(minutes=1))
datetime.timedelta(seconds=60)
>>> as_timedelta(90)
datetime.timedelta(seconds=90)
>>> as_timedelta(1.5)
datetime.timedelta(seconds=1, microseconds=500000)
A `bool` is an `int` in Python, but it is never a valid duration:
>>> as_timedelta(True)
Traceback (most recent call last):
TypeError: expected a duration, got bool: True
>>> as_timedelta("1")
Traceback (most recent call last):
TypeError: expected a timedelta, int, or float duration, got str
"""
if isinstance(duration, dt.timedelta):
return duration
@@ -27,7 +43,13 @@ def as_timedelta(duration: Duration) -> dt.timedelta:
def as_seconds(duration: Duration) -> float:
"""Coerce a `Duration` into a floating-point number of seconds."""
"""Coerce a `Duration` into a floating-point number of seconds.
>>> as_seconds(90)
90.0
>>> as_seconds(dt.timedelta(minutes=1, milliseconds=500))
60.5
"""
return as_timedelta(duration).total_seconds()

View File

@@ -242,7 +242,7 @@ class TerminalLogger(AbstractLogger):
elapsed = time.monotonic() - self._start_time
if "machine" in attributes:
message = f"{attributes['machine']}: {message}"
return f"[{elapsed:.2f}s] {message}"
return Style.DIM + f"[{elapsed:.2f}s]" + Style.RESET_ALL + f" {message}" # ty: ignore[unsupported-operator]
@staticmethod
def _eprint(*args: object, **kwargs: Any) -> None:
@@ -322,10 +322,27 @@ class XMLLogger(AbstractLogger):
self.xml.endDocument()
self.logfile_handle.close()
def sanitise(self, message: str) -> str:
@staticmethod
def sanitise(message: str) -> str:
r"""Remove the characters of Unicode's "Other" categories, such as
control characters, from serial output before it is written to XML.
>>> XMLLogger.sanitise("booting\x00...\x07")
'booting...'
>>> XMLLogger.sanitise("Grüße")
'Grüße'
"""
return "".join(ch for ch in message if unicodedata.category(ch)[0] != "C")
def maybe_prefix(self, message: str, attributes: dict[str, str]) -> str:
@staticmethod
def maybe_prefix(message: str, attributes: dict[str, str]) -> str:
"""Prefix the message with the name of the machine, if there is one.
>>> XMLLogger.maybe_prefix("booting", {"machine": "server"})
'server: booting'
>>> XMLLogger.maybe_prefix("booting", {})
'booting'
"""
if "machine" in attributes:
return f"{attributes['machine']}: {message}"
return message

View File

@@ -5,7 +5,7 @@ import os
import platform
import queue
import re
import select
import selectors
import shlex
import shutil
import socket
@@ -139,7 +139,22 @@ XDOTOOL_MODIFIER_ALIASES = {
def xdotool_key_sequence(key: str) -> str:
"""Translate a QEMU-style key chord to xdotool syntax.
For example, "ctrl-alt-ret" becomes "ctrl+alt+Return".
>>> xdotool_key_sequence("ctrl-alt-ret")
'ctrl+alt+Return'
>>> xdotool_key_sequence("meta_l-x")
'super+x'
>>> xdotool_key_sequence("alt-f4")
'alt+F4'
A modifier on its own is a regular key:
>>> xdotool_key_sequence("shift")
'Shift_L'
A "-" that follows a modifier is the key itself, not a separator:
>>> xdotool_key_sequence("ctrl--")
'ctrl+-'
"""
parts = key.split("-")
modifiers = []
@@ -154,9 +169,42 @@ def xdotool_key_sequence(key: str) -> str:
def make_command(args: list) -> str:
"""Join arguments into a shell command, quoting each one as a single word.
>>> make_command(["echo", "a b", 1])
"echo 'a b' 1"
>>> make_command(["ls", "*"])
"ls '*'"
"""
return " ".join(map(shlex.quote, (map(str, args))))
def parse_systemctl_property(line: str) -> tuple[str, str] | None:
"""Split a line of `systemctl show` output into its property and value.
>>> parse_systemctl_property("Id=foo.service")
('Id', 'foo.service')
Only the first "=" separates the two, and values can be empty:
>>> parse_systemctl_property("Environment=A=1 B=2")
('Environment', 'A=1 B=2')
>>> parse_systemctl_property("Description=")
('Description', '')
Lines that aren't properties yield `None`:
>>> parse_systemctl_property("") is None
True
>>> parse_systemctl_property("no property") is None
True
"""
match = re.match(r"^([^=]+)=(.*)$", line)
if match is None:
return None
return match[1], match[2]
def retry(
fn: Callable,
timeout_seconds: int | dt.timedelta | None = None,
@@ -217,6 +265,13 @@ class QemuStartCommand:
These Nix commands have the particular characteristic that the
machine name can be extracted out of them via a regex match.
(Admittedly a _very_ implicit contract, TODO fix this eventually.)
>>> QemuStartCommand("/nix/store/hash-nixos-vm/bin/run-foo-vm").machine_name
'foo'
>>> QemuStartCommand("run-foo-bar-vm").machine_name
'foo-bar'
>>> QemuStartCommand("/some/script").machine_name
'machine'
"""
match = re.search("run-(.+)-vm$", self._cmd)
name = "machine"
@@ -397,6 +452,29 @@ class BaseMachine(ABC):
"""Shutdown the machine gracefully, waiting for it to exit."""
...
@staticmethod
def _systemctl_command(q: str, user: str | None = None) -> str:
r"""Build a `systemctl` command, or a `systemctl --user` one for `user`.
>>> print(BaseMachine._systemctl_command("list-jobs --no-pager"))
systemctl list-jobs --no-pager
>>> print(BaseMachine._systemctl_command("list-jobs --no-pager", "alice"))
su -l alice --shell /bin/sh -c $'XDG_RUNTIME_DIR=/run/user/`id -u` systemctl --user list-jobs --no-pager'
Single quotes survive the surrounding `$'...'`:
>>> print(BaseMachine._systemctl_command("show 'foo bar'", "alice"))
su -l alice --shell /bin/sh -c $'XDG_RUNTIME_DIR=/run/user/`id -u` systemctl --user show \'foo bar\''
"""
if user is None:
return f"systemctl {q}"
escaped = q.replace("'", "\\'")
return (
f"su -l {user} --shell /bin/sh -c "
"$'XDG_RUNTIME_DIR=/run/user/`id -u` "
f"systemctl --user {escaped}'"
)
def systemctl(self, q: str, user: str | None = None) -> tuple[int, str]:
"""
Runs `systemctl` commands with optional support for
@@ -411,14 +489,7 @@ class BaseMachine(ABC):
machine.systemctl("list-jobs --no-pager", "any-user")
```
"""
if user is not None:
q = q.replace("'", "\\'")
return self.execute(
f"su -l {user} --shell /bin/sh -c "
"$'XDG_RUNTIME_DIR=/run/user/`id -u` "
f"systemctl --user {q}'"
)
return self.execute(f"systemctl {q}")
return self.execute(self._systemctl_command(q, user))
def wait_for_unit(
self,
@@ -467,17 +538,10 @@ class BaseMachine(ABC):
+ f" failed with exit code {status}"
)
line_pattern = re.compile(r"^([^=]+)=(.*)$")
def tuple_from_line(line: str) -> tuple[str, str]:
match = line_pattern.match(line)
assert match is not None
return match[1], match[2]
return dict(
tuple_from_line(line)
parsed
for line in lines.split("\n")
if line_pattern.match(line)
if (parsed := parse_systemctl_property(line)) is not None
)
def get_unit_property(
@@ -505,12 +569,12 @@ class BaseMachine(ABC):
f"produced invalid output: {lines}"
)
line_pattern = re.compile(r"^([^=]+)=(.*)$")
match = line_pattern.match(lines)
assert match is not None, invalid_output_message
parsed = parse_systemctl_property(lines)
assert parsed is not None, invalid_output_message
assert match[1] == property, invalid_output_message
return match[2]
name, value = parsed
assert name == property, invalid_output_message
return value
def require_unit_state(self, unit: str, require_state: str = "active") -> None:
"""
@@ -742,7 +806,7 @@ class BaseMachine(ABC):
)
if ret.returncode != 0:
raise MachineError(
f"Cannot convert screenshot (pnmtopng returned code {ret.returncode})"
f"{self.name}: Cannot convert screenshot (pnmtopng returned code {ret.returncode})"
)
def get_screen_text_variants(self) -> list[str]:
@@ -1178,15 +1242,18 @@ class QemuMachine(BaseMachine):
"""Debugging: Dump the contents of the TTY<n>"""
self.execute(f"fold -w 80 /dev/vcs{tty} | systemd-cat")
def _execute(
self,
command: str,
check_return: bool = True,
check_output: bool = True,
timeout: dt.timedelta | None = dt.timedelta(minutes=15),
) -> tuple[int, str]:
self.connect()
@staticmethod
def _shell_command(command: str, timeout: dt.timedelta | None) -> str:
"""Build the line that runs `command` over the shell of the machine.
The command runs with strict shell options, and its output gets
base64-encoded so that it can be read back as a single line:
>>> print(QemuMachine._shell_command("echo 'hi'", dt.timedelta(seconds=10)))
timeout 10.0 bash -c 'set -euo pipefail; echo '"'"'hi'"'"'' | (base64 -w 0; echo)
>>> print(QemuMachine._shell_command("true", None).strip())
bash -c 'set -euo pipefail; true' | (base64 -w 0; echo)
"""
# Always run command with shell opts
command = f"set -euo pipefail; {command}"
@@ -1196,12 +1263,19 @@ class QemuMachine(BaseMachine):
# While sh is bash on NixOS, this is not the case for every distro.
# We explicitly call bash here to allow for the driver to boot other distros as well.
out_command = (
f"{timeout_str} bash -c {shlex.quote(command)} | (base64 -w 0; echo)\n"
)
return f"{timeout_str} bash -c {shlex.quote(command)} | (base64 -w 0; echo)"
def _execute(
self,
command: str,
check_return: bool = True,
check_output: bool = True,
timeout: dt.timedelta | None = dt.timedelta(minutes=15),
) -> tuple[int, str]:
self.connect()
assert self.shell
self.shell.send(out_command.encode())
self.shell.send(f"{self._shell_command(command, timeout)}\n".encode())
if not check_output:
return (-2, "")
@@ -1335,16 +1409,6 @@ class QemuMachine(BaseMachine):
Wait for a connection to the guest root shell
"""
def shell_ready(timeout: dt.timedelta) -> bool:
"""We sent some data from the backdoor service running on the guest
to indicate that the backdoor shell is ready.
As soon as we read some data from the socket here, we assume that
our root shell is operational.
"""
assert self.shell
(ready, _, _) = select.select([self.shell], [], [], timeout.total_seconds())
return bool(ready)
if self.connected:
return
@@ -1354,9 +1418,15 @@ class QemuMachine(BaseMachine):
assert self.shell
tic = time.monotonic()
sel = selectors.DefaultSelector()
sel.register(self.shell, selectors.EVENT_READ)
for _ in range(10):
if shell_ready(timeout=dt.timedelta(seconds=30)):
# We sent some data from the backdoor service running on the guest
# to indicate that the backdoor shell is ready.
# As soon as we read some data from the socket here, we assume that
# our root shell is operational.
if sel.select(timeout=30):
break
self.log("Guest root shell did not produce any data yet...")
self.log(
@@ -1477,6 +1547,7 @@ class QemuMachine(BaseMachine):
s = socket.socket(family=socket.AF_UNIX, type=socket.SOCK_STREAM)
s.bind(str(path))
s.listen(1)
s.setblocking(False)
return s
monitor_socket = create_socket(clear(self.monitor_path))
@@ -1498,8 +1569,10 @@ class QemuMachine(BaseMachine):
would block forever if QEMU exits before connecting.
"""
assert self.process
while True:
readable, _, _ = select.select([sock], [], [], 1.0)
sel = selectors.DefaultSelector()
sel.register(sock, selectors.EVENT_READ)
for _ in range(60):
readable = sel.select(1)
if readable:
conn, _ = sock.accept()
return conn
@@ -1509,8 +1582,11 @@ class QemuMachine(BaseMachine):
if self.process.stdout:
output = self.process.stdout.read().decode(errors="ignore")
raise MachineError(
f"QEMU process exited with code {rc} before connecting to {name} socket.\n{output}"
f"{self.name}: QEMU process exited with code {rc} before connecting to {name} socket.\n{output}"
)
raise MachineError(
f"{self.name}: Timed out waiting for QEMU connection on {name} socket"
)
self.monitor = accept_or_fail(monitor_socket, "monitor")
self.shell = accept_or_fail(shell_socket, "shell")
@@ -1690,7 +1766,19 @@ class NspawnMachine(BaseMachine):
vnc: NspawnVnc | None
@staticmethod
def machine_name_from_start_command(start_command: str) -> str:
def _machine_name_from_start_command(start_command: str) -> str:
"""Extract the machine name from a start script of a container.
>>> NspawnMachine._machine_name_from_start_command(
... "/nix/store/hash-container/bin/run-foo-nspawn"
... )
'foo'
>>> NspawnMachine._machine_name_from_start_command("run-foo-bar-nspawn")
'foo-bar'
>>> NspawnMachine._machine_name_from_start_command("/some/script")
Traceback (most recent call last):
AssertionError: Could not extract node name from /some/script
"""
match = re.search("run-(.+)-nspawn", os.path.basename(start_command))
assert match is not None, f"Could not extract node name from {start_command}"
return match.group(1)
@@ -1710,7 +1798,7 @@ class NspawnMachine(BaseMachine):
# See analogous TODO in `QemuStartCommand::machine_name`.
super().__init__(
out_dir=out_dir,
name=name or self.machine_name_from_start_command(start_command),
name=name or self._machine_name_from_start_command(start_command),
logger=logger,
callbacks=callbacks,
tmp_dir=tmp_dir,
@@ -1743,6 +1831,33 @@ class NspawnMachine(BaseMachine):
with self.nested("waiting for the X11 server"):
retry(check_x, as_timedelta(timeout))
@staticmethod
def _xdotool_command(key: str) -> str:
"""Build the xdotool command that presses a QEMU-style key.
Literal characters get typed to avoid key syntax, e.g. "+" as a chord
separator:
>>> print(NspawnMachine._xdotool_command("a"))
xdotool type --clearmodifiers --delay 0 -- a
>>> print(NspawnMachine._xdotool_command("+"))
xdotool type --clearmodifiers --delay 0 -- +
Everything else is a key chord:
>>> print(NspawnMachine._xdotool_command("ctrl-alt-ret"))
xdotool key --clearmodifiers ctrl+alt+Return
>>> print(NspawnMachine._xdotool_command("\\n"))
xdotool key --clearmodifiers Return
"""
if len(key) == 1 and key.isprintable():
return make_command(
["xdotool", "type", "--clearmodifiers", "--delay", 0, "--", key]
)
return make_command(
["xdotool", "key", "--clearmodifiers", xdotool_key_sequence(key)]
)
def send_key(
self,
key: str,
@@ -1752,25 +1867,7 @@ class NspawnMachine(BaseMachine):
_warn_if_numeric_duration(delay, "send_key")
context = self.nested(f"sending key {repr(key)}") if log else nullcontext()
with context:
# Type literal characters to avoid key syntax, e.g. "+" as a chord separator.
if len(key) == 1 and key.isprintable():
command = [
"xdotool",
"type",
"--clearmodifiers",
"--delay",
0,
"--",
key,
]
else:
command = [
"xdotool",
"key",
"--clearmodifiers",
xdotool_key_sequence(key),
]
self.succeed(make_command(command))
self.succeed(self._xdotool_command(key))
if delay is not None:
time.sleep(as_seconds(delay))
@@ -1799,7 +1896,7 @@ class NspawnMachine(BaseMachine):
ret = subprocess.run(["xwdtopnm", xwd_path], stdout=ppm)
if ret.returncode != 0:
raise MachineError(
f"Cannot convert screenshot (xwdtopnm returned code {ret.returncode})"
f"{self.name}: Cannot convert screenshot (xwdtopnm returned code {ret.returncode})"
)
yield ppm_path
@@ -1836,6 +1933,28 @@ class NspawnMachine(BaseMachine):
def is_up(self) -> bool:
return self.process is not None
@staticmethod
def _parse_sd_notify(data: bytes) -> tuple[bool, int | None]:
"""Extract readiness and the leader PID from an `sd_notify` datagram.
>>> NspawnMachine._parse_sd_notify(b"READY=1\\n")
(True, None)
>>> NspawnMachine._parse_sd_notify(b"X_NSPAWN_LEADER_PID=42\\n")
(False, 42)
>>> NspawnMachine._parse_sd_notify(b"X_NSPAWN_LEADER_PID=42\\nREADY=1\\n")
(True, 42)
>>> NspawnMachine._parse_sd_notify(b"STATUS=Starting up\\n")
(False, None)
"""
ready = False
leader_pid = None
for line in data.decode(errors="replace").splitlines():
if line == "READY=1":
ready = True
if line.startswith("X_NSPAWN_LEADER_PID="):
leader_pid = int(line.split("=")[1])
return ready, leader_pid
def _drain_notify_socket(self) -> None:
"""Continuously drain the container's `sd_notify` socket (NOTIFY_SOCKET)
for the whole lifetime of the container, recording readiness and the
@@ -1866,13 +1985,7 @@ class NspawnMachine(BaseMachine):
continue
except OSError:
break
ready = False
leader_pid = None
for line in data.decode(errors="replace").splitlines():
if line == "READY=1":
ready = True
if line.startswith("X_NSPAWN_LEADER_PID="):
leader_pid = int(line.split("=")[1])
ready, leader_pid = self._parse_sd_notify(data)
if ready or leader_pid is not None:
with self._notify_lock:
if ready:
@@ -1898,7 +2011,9 @@ class NspawnMachine(BaseMachine):
# Poll the socket until we have the container leader PID
while True:
if self.process.poll() is not None:
raise MachineError("systemd-nspawn process exited unexpectedly")
raise MachineError(
f"{self.name}: systemd-nspawn process exited unexpectedly"
)
with self._notify_lock:
is_ready = self._notify_ready

View File

@@ -12,6 +12,23 @@ logger = logging.getLogger(__name__)
class QMPAPIError(RuntimeError):
"""An error response from the QMP API.
>>> error = {"class": "GenericError", "desc": "boom"}
>>> print(QMPAPIError({"error": error, "id": 3}))
<QMP API error related to transaction 3 [GenericError]: boom>
The transaction id is absent from errors that the server hits before it
could read the id:
>>> print(QMPAPIError({"error": error}))
<QMP API error related to transaction None [GenericError]: boom>
>>> QMPAPIError({"error": {"class": "GenericError"}})
Traceback (most recent call last):
RuntimeError: Malformed QMP API error response
"""
def __init__(self, message: dict[str, Any]):
assert "error" in message, "Not an error message!"
error = message["error"]

View File

@@ -13,6 +13,35 @@ class PollingConditionError(Exception):
class PollingCondition:
"""A condition that gets checked periodically while a test runs.
The description comes from the `description` argument. Without one, it
falls back to the docstring of the condition, and then to its name:
>>> from test_driver.logger import CompositeLogger
>>> logger = CompositeLogger([])
>>> def service_runs():
... "The service is running"
>>> def no_docstring():
... pass
>>> PollingCondition(service_runs, logger, description="custom").description
'custom'
>>> PollingCondition(service_runs, logger).description
'The service is running'
>>> PollingCondition(no_docstring, logger).description
'no_docstring'
Log messages include the description:
>>> condition = PollingCondition(service_runs, logger)
>>> condition.nested_message
"Checking polling condition 'The service is running'"
>>> condition.status_message(True)
'Polling condition succeeded: The service is running'
>>> condition.status_message(False)
'Polling condition failed: The service is running'
"""
condition: Callable[[], bool]
interval: dt.timedelta
description: str | None

View File

@@ -30,7 +30,6 @@ let
cfg = config.fonts.fontconfig;
fcBool = x: "<bool>" + (lib.boolToString x) + "</bool>";
pkg = pkgs.fontconfig;
# configuration file to read fontconfig cache
# priority 0
@@ -49,7 +48,7 @@ let
inherit fontconfig;
fontDirectories = config.fonts.packages;
};
cache = makeCache pkgs.fontconfig;
cache = makeCache cfg.package;
cache32 = makeCache pkgs.pkgsi686Linux.fontconfig;
in
pkgs.writeText "fc-00-nixos-cache.conf" ''
@@ -209,7 +208,7 @@ let
# Replace default linked config with a different variant
replaceDefaultConfig = defaultConfig: newConfig: ''
rm $dst/${defaultConfig}
ln -s ${pkg.out}/share/fontconfig/conf.avail/${newConfig} \
ln -s ${cfg.package.out}/share/fontconfig/conf.avail/${newConfig} \
$dst/
'';
@@ -224,7 +223,7 @@ let
mkdir -p $dst
# fonts.conf
ln -s ${pkg.out}/etc/fonts/fonts.conf \
ln -s ${cfg.package.out}/etc/fonts/fonts.conf \
$dst/../fonts.conf
# TODO: remove this legacy symlink once people stop using packages built before #95358 was merged
mkdir -p $out/etc/fonts/2.11
@@ -232,7 +231,7 @@ let
$out/etc/fonts/2.11/fonts.conf
# fontconfig default config files
ln -s ${pkg.out}/etc/fonts/conf.d/*.conf \
ln -s ${cfg.package.out}/etc/fonts/conf.d/*.conf \
$dst/
${lib.optionalString (!cfg.antialias) (
@@ -352,6 +351,8 @@ in
'';
};
package = lib.mkPackageOption pkgs "fontconfig" { };
confPackages = lib.mkOption {
internal = true;
type = with lib.types; listOf path;
@@ -627,14 +628,14 @@ in
};
config = lib.mkMerge [
(lib.mkIf cfg.enable {
environment.systemPackages = [ pkgs.fontconfig ];
environment.systemPackages = [ cfg.package ];
environment.etc.fonts.source = "${fontconfigEtc}/etc/fonts/";
security.apparmor.includes."abstractions/fonts" = ''
# fonts.conf
r ${pkg.out}/etc/fonts/fonts.conf,
r ${cfg.package.out}/etc/fonts/fonts.conf,
# fontconfig default config files
r ${pkg.out}/etc/fonts/conf.d/*.conf,
r ${cfg.package.out}/etc/fonts/conf.d/*.conf,
# 00-nixos-cache.conf
r ${cacheConf},
@@ -644,7 +645,7 @@ in
# 50-user.conf
${lib.optionalString cfg.includeUserConf ''
r ${pkg.out}/etc/fonts/conf.d.bak/50-user.conf,
r ${cfg.package.out}/etc/fonts/conf.d.bak/50-user.conf,
''}
# local.conf (indirect priority 51)

View File

@@ -89,6 +89,7 @@ let
"systemd-fsck-root.service"
"systemd-growfs@.service"
"systemd-growfs-root.service"
"systemd-loop@.service"
"systemd-remount-fs.service"
"systemd-pstore.service"
"local-fs.target"

View File

@@ -97,6 +97,7 @@ let
"systemd-factory-reset-complete.service"
"factory-reset-now.target"
"systemd-fsck@.service"
"systemd-loop@.service"
"systemd-halt.service"
"systemd-hibernate-resume.service"
"systemd-journald-audit.socket"
@@ -613,6 +614,7 @@ in
"${cfg.package}/lib/systemd/systemd-bsod"
"${cfg.package}/lib/systemd/systemd-sysroot-fstab-check"
"${cfg.package}/lib/systemd/systemd-factory-reset"
"${cfg.package}/bin/systemd-dissect"
# generators
"${cfg.package}/lib/systemd/system-generators/systemd-debug-generator"

View File

@@ -60,6 +60,10 @@
# NixOS.
systemd.services."systemd-pcrlogin@".unitConfig.ConditionPathExists =
"/run/systemd/nvpcr/login.auth";
systemd.sockets."systemd-pcrextend" = {
wantedBy = [ "sockets.target" ];
};
}
)
(
@@ -101,6 +105,10 @@
"${cfg.package}/lib/systemd/system-generators/systemd-tpm2-generator"
"${cfg.package}/lib/systemd/systemd-pcrextend"
];
boot.initrd.systemd.sockets."systemd-pcrextend" = {
wantedBy = [ "sockets.target" ];
};
}
)
(

View File

@@ -28,6 +28,8 @@ let
consoles = lib.concatMapStringsSep " " (c: "console=${c}") cfg.qemu.consoles;
runScript = hostPkgs.callPackage ./run-virtiofsd-qemu { };
driveOptions =
{ ... }:
{
@@ -326,33 +328,28 @@ let
''
)}
${lib.optionalString useVirtiofs ''
echo "Starting virtiofs daemons..."
NIX_VIRTIOFS_DIR=$(mktemp -d)
${lib.concatLines (
lib.mapAttrsToList (tag: share: ''
${lib.getExe hostPkgs.virtiofsd} \
--socket-path="$NIX_VIRTIOFS_DIR"/"${tag}" \
--shared-dir="$(readlink -f "${share.source}")" \
${if share.writable then "--writeback" else "--readonly"} \
--sandbox=none \
--seccomp=none \
--cache=${share.cache} \
--no-announce-submounts \
--translate-uid=host:65534:0:1 \
--translate-gid=host:65534:0:1 \
&
'') cfg.sharedDirectories
)}
''}
export NIX_VIRTIOFS_DIR=$(mktemp -d)
# Start QEMU.
exec ${
qemu-common.qemuBinaryWith {
qemuPkg = qemu;
forceAccel = cfg.qemu.forceAccel;
}
} \
exec ${lib.getExe runScript} \
${
lib.optionalString (useVirtiofs && cfg.sharedDirectories != { })
"--virtiofs-paths ${
lib.concatStringsSep " " (
lib.mapAttrsToList (
tag: share:
"${tag}:${share.source}:${share.cache}:${if share.writable then "writeback" else "readonly"}"
) cfg.sharedDirectories
)
}"
} \
-- \
${
qemu-common.qemuBinaryWith {
qemuPkg = qemu;
forceAccel = cfg.qemu.forceAccel;
}
} \
-name ${config.system.name} \
-m ${toString config.virtualisation.memorySize} \
-smp ${toString config.virtualisation.cores} \

View File

@@ -0,0 +1,3 @@
{ python3Packages }:
python3Packages.callPackage ./package.nix { }

View File

@@ -0,0 +1,49 @@
{
lib,
buildPythonApplication,
virtiofsd,
ruff,
setuptools,
ty,
}:
buildPythonApplication {
pname = "run-virtiofsd-qemu";
version = "1.0";
pyproject = true;
src = ./.;
postPatch = ''
substituteInPlace ./run_virtiofsd_qemu/__init__.py \
--replace-fail "@virtiofsd@" "${lib.getExe virtiofsd}"
'';
build-system = [
setuptools
];
propagatedBuildInputs = [
virtiofsd
];
doCheck = true;
nativeCheckInputs = [
ruff
ty
];
checkPhase = ''
echo -e "\x1b[32m## run ty\x1b[0m"
ty check --error-on-warning run_virtiofsd_qemu
echo -e "\x1b[32m## run ruff check\x1b[0m"
ruff check .
echo -e "\x1b[32m## run ruff format\x1b[0m"
ruff format --check --diff .
'';
meta = {
mainProgram = "run-virtiofsd-qemu";
};
}

View File

@@ -0,0 +1,13 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
[project]
name = "run-virtiofsd-qemu"
version = "1.0"
[project.scripts]
run-virtiofsd-qemu = "run_virtiofsd_qemu:main"
[tool.setuptools.packages]
find = {}

View File

@@ -0,0 +1,120 @@
import argparse
import asyncio
import logging
import signal
import subprocess
from contextlib import AsyncExitStack, asynccontextmanager
from os import environ
from pathlib import Path
from sys import exit
logger = logging.getLogger(__name__)
def fatal_error(msg: str):
logger.error(msg)
exit(1)
@asynccontextmanager
async def virtiofsd(virtiofs_dir: Path, tag: str, source: Path, cache: str, flag: str):
socket_path = virtiofs_dir / tag
proc = await asyncio.create_subprocess_exec(
"@virtiofsd@",
f"--socket-path={socket_path}",
f"--shared-dir={source.resolve()}",
f"--{flag}",
"--sandbox=none",
"--seccomp=none",
f"--cache={cache}",
"--no-announce-submounts",
"--translate-uid=host:65534:0:1",
"--translate-gid=host:65534:0:1",
)
loop = asyncio.get_running_loop()
while not await loop.run_in_executor(None, lambda: socket_path.exists()):
if proc.returncode is not None:
fatal_error(f"virtiofsd process for '{tag}' failed to start. Aborting.")
await asyncio.sleep(1)
yield
try:
proc.terminate()
async with asyncio.timeout(2):
await proc.communicate()
except ProcessLookupError:
pass
except subprocess.TimeoutExpired:
logger.warning(
f"virtiofsd process for '{tag}' still running after SIGTERM. Killing."
)
proc.kill()
await proc.communicate()
def extract_arguments() -> tuple[list[tuple[str, Path, str, str]], list[str]]:
parser = argparse.ArgumentParser()
parser.add_argument(
"--virtiofs-paths",
nargs="+",
help="Paths to be exposed with virtiofsd in the format 'tag:source:mode:cache'",
)
args, qemu_args = parser.parse_known_args()
if not qemu_args:
logger.warning("No command to run specified, exiting")
exit(0)
if qemu_args[0] != "--":
fatal_error(
"Usage error: expected command to run separated with '--' from the args pased to this program!"
)
qemu_args.pop(0)
if not qemu_args:
logger.warning("No command to run specified, exiting")
exit(0)
defs = []
for defin in args.virtiofs_paths:
args = defin.split(":")
if len(args) != 4:
fatal_error(
f"--virtio-paths definition '{defin}' doesn't have four params separated by ':'!"
)
defs.append((args[0], Path(args[1]), args[2], args[3]))
return defs, qemu_args
async def amain():
logging.basicConfig(format="%(levelname)s: %(funcName)s: %(message)s")
args, qemu_args = extract_arguments()
try:
virtiofs_dir = Path(environ["NIX_VIRTIOFS_DIR"])
except KeyError:
fatal_error("NIX_VIRTIOFS_DIR is unset!")
async with AsyncExitStack() as virtiofsds:
await asyncio.gather(
*(
virtiofsds.enter_async_context(virtiofsd(virtiofs_dir, *defin))
for defin in args
)
)
proc = await asyncio.create_subprocess_exec(*qemu_args)
loop = asyncio.get_running_loop()
loop.add_signal_handler(signal.SIGTERM, lambda: proc.terminate())
loop.add_signal_handler(signal.SIGINT, lambda: proc.terminate())
await proc.communicate()
def main():
asyncio.run(amain())
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -0,0 +1,4 @@
{
pkgs ? import ../../../.. { },
}:
pkgs.callPackage ./default.nix { }

View File

@@ -31,6 +31,12 @@ in
displayManager.lightdm.extraConfig = ''
logind-check-graphical = false
'';
# `wait_for_x()` establishing a connection that is being teared down
# before having any other connection to X11 causes the X server to reset
# itself. This flag prevents that behavior.
# Context: https://github.com/NixOS/nixpkgs/pull/578540#issuecomment-6593586095
displayManager.xserverArgs = lib.mkAfter [ "-noreset" ];
})
];

View File

@@ -19,15 +19,25 @@
boot.initrd.availableKernelModules = [ "tpm_tis" ];
environment.systemPackages = with pkgs; [ cryptsetup ];
environment.systemPackages = with pkgs; [
cryptsetup
tpm2-tools
];
specialisation.boot-luks.configuration = {
boot.initrd.luks.devices = lib.mkVMOverride {
cryptroot = {
device = "/dev/vdb";
crypttabExtraOpts = [ "tpm2-device=auto" ];
crypttabExtraOpts = [
"tpm2-device=auto"
"tpm2-measure-pcr=yes"
];
};
};
# The volume key measurement is only performed when the OS is explicitly
# marked as measured (or booted through a measuring kernel stub).
boot.kernelParams = [ "systemd.tpm2_measured_os=1" ];
virtualisation.rootDevice = "/dev/mapper/cryptroot";
};
};
@@ -54,5 +64,11 @@
# Boot and decrypt the disk
machine.wait_for_unit("multi-user.target")
assert "/dev/mapper/cryptroot on / type ext4" in machine.succeed("mount")
# The volume key has to be measured into PCR 15 through the
# io.systemd.PCRExtend socket during initrd.
machine.succeed("grep -q volume-key /run/log/systemd/tpm2-measure.log")
pcr = machine.succeed("tpm2_pcrread sha256:15")
assert "15: 0x0000000000000000000000000000000000000000000000000000000000000000" not in pcr, pcr
'';
}

View File

@@ -109,7 +109,7 @@ fn add_dependencies<P: AsRef<Path> + AsRef<OsStr> + std::fmt::Debug>(
.position(|x| *x == 0)
.unwrap_or(note.desc.len())];
let parsed = serde_json::from_slice::<Vec<DLOpenNote>>(payload)?;
for mut parsed_note in parsed {
for parsed_note in parsed {
if dlopen.use_priority
>= parsed_note.priority.unwrap_or(DLOpenPriority::Recommended)
|| parsed_note
@@ -117,7 +117,9 @@ fn add_dependencies<P: AsRef<Path> + AsRef<OsStr> + std::fmt::Debug>(
.map(|f| dlopen.features.contains(&f))
.unwrap_or(false)
{
dlopen_libraries.append(&mut parsed_note.soname);
// SONAMEs in one note are ordered alternatives, not
// independent dependencies.
dlopen_libraries.push(parsed_note.soname);
}
}
}
@@ -137,39 +139,38 @@ fn add_dependencies<P: AsRef<Path> + AsRef<OsStr> + std::fmt::Debug>(
.map(|p| Box::<Path>::from(Path::new(p)))
.collect::<Vec<_>>();
for line in elf
for sonames in elf
.libraries
.into_iter()
.map(|s| s.to_string())
.map(|s| vec![s.to_string()])
.chain(dlopen_libraries)
{
let mut found = false;
for path in &rpaths_as_path {
let lib = path.join(&line);
if lib.exists() {
// No need to recurse. The queue will bring it back round.
queue.push_back(StorePath {
path: Box::from(lib.as_path()),
dlopen: dlopen.clone(),
});
found = true;
break;
}
}
if !found {
let library = sonames
.iter()
.flat_map(|soname| rpaths_as_path.iter().map(move |path| path.join(soname)))
.find(|lib| lib.exists());
if let Some(lib) = library {
// No need to recurse. The queue will bring it back round.
queue.push_back(StorePath {
path: lib.into_boxed_path(),
dlopen: dlopen.clone(),
});
} else {
// In Nix, glibc's own libraries lack rpath entries pointing to
// themselves, so the dynamic linker (ld-linux-*.so.*) and libc.so.*
// can never be resolved through rpath alone. They are always present
// in the initrd: the linker via elf.interpreter above, and libc via
// at least one binary's rpath. Suppress these known-benign cases.
// See also: the ld*.so.? skip in stage-1.nix findLibs.
let is_glibc_runtime = (line.starts_with("ld-") && line.contains(".so"))
|| line.starts_with("libc.so");
let is_glibc_runtime = matches!(
sonames.as_slice(),
[s] if (s.starts_with("ld-") && s.contains(".so")) || s.starts_with("libc.so")
);
if !is_glibc_runtime {
eprintln!(
"Warning: Couldn't satisfy dependency {} for {:?}",
line,
sonames.join(" or "),
OsStr::new(&source)
);
}
@@ -314,13 +315,10 @@ fn main() -> eyre::Result<()> {
let args: Vec<String> = env::args().collect();
let contents =
fs::read(&args[1]).wrap_err_with(|| format!("failed to open file {:?}", &args[1]))?;
let input = serde_json::from_slice::<Vec<StoreInput>>(&contents)
.wrap_err_with(|| {
let text = String::from_utf8_lossy(&contents);
format!("failed to parse JSON '{}' in {:?}",
text,
&args[1])
})?;
let input = serde_json::from_slice::<Vec<StoreInput>>(&contents).wrap_err_with(|| {
let text = String::from_utf8_lossy(&contents);
format!("failed to parse JSON '{}' in {:?}", text, &args[1])
})?;
let output = &args[2];
let out_path = Path::new(output);

View File

@@ -7,6 +7,8 @@
bluez,
tcl,
acl,
polkitSupport ? lib.meta.availableOn stdenv.hostPlatform polkit,
polkit,
kmod,
coreutils,
shadow,
@@ -42,6 +44,7 @@ stdenv.mkDerivation (finalAttrs: {
tcl # For TCL bindings
]
++ lib.optional alsaSupport alsa-lib
++ lib.optional polkitSupport polkit
++ lib.optional systemdSupport systemdMinimal;
doInstallCheck = true;
@@ -76,6 +79,7 @@ stdenv.mkDerivation (finalAttrs: {
"--with-writable-directory=/run/brltty"
"--with-updatable-directory=/var/lib/brltty"
"--with-api-socket-path=/var/lib/BrlAPI"
(lib.enableFeature polkitSupport "polkit")
];
installFlags = [
"install-systemd"

View File

@@ -21,11 +21,11 @@
stdenv.mkDerivation (finalAttrs: {
pname = "btrfs-progs";
version = "7.1";
version = "7.3";
src = fetchurl {
url = "mirror://kernel/linux/kernel/people/kdave/btrfs-progs/btrfs-progs-v${finalAttrs.version}.tar.xz";
hash = "sha256-0fVcwpcTmMkULqp50gPmPVhqO0uGf5VmZKHWgyLNTjQ=";
hash = "sha256-03tjQD0l/gJXBMdY5qPaCXmuUMMMXwKqeORBDns0bjo=";
};
nativeBuildInputs = [

View File

@@ -10,14 +10,14 @@
stdenv.mkDerivation (finalAttrs: {
pname = "libslirp";
version = "4.9.5";
version = "4.9.6";
src = fetchFromGitLab {
domain = "gitlab.freedesktop.org";
owner = "slirp";
repo = "libslirp";
tag = "v${finalAttrs.version}";
hash = "sha256-iWu3/Klsm8e9MH147BJhn9Vqyneq5ROqPvll1cnieL0=";
hash = "sha256-nfR5eQrcpbkmn2tx/8l649NVPOsIFSFSBkW/vw0meCM=";
};
__structuredAttrs = true;

View File

@@ -108,6 +108,8 @@ chroot_add_resolv_conf "$mountPoint" || echo "$0: failed to set up resolv.conf"
chroot "$mountPoint" "$system/sw/bin/systemd-tmpfiles" --create --remove -E 2> /dev/null || true
)
unset TMPDIR
# HOSTNAME is unset to not accidentally build a nixos configuration named like the ISO.
# NIX_REMOTE is unset to not use the ISOs nix daemon by accident.
unset HOSTNAME NIX_REMOTE TMPDIR
exec chroot "$mountPoint" "${command[@]}"

View File

@@ -3,10 +3,12 @@
lib,
bash,
installShellFiles,
runCommand,
buildPackages,
nixosTests,
}:
stdenvNoCC.mkDerivation {
stdenvNoCC.mkDerivation (finalAttrs: {
pname = "nixos-firewall-tool";
version = lib.trivial.release;
src = builtins.filterSource (name: _: !(lib.hasSuffix ".nix" name)) ./.;
@@ -14,7 +16,6 @@ stdenvNoCC.mkDerivation {
strictDeps = true;
buildInputs = [ bash ];
nativeBuildInputs = [ installShellFiles ];
nativeCheckInputs = [ buildPackages.shellcheck-minimal ];
postPatch = ''
patchShebangs --host nixos-firewall-tool
@@ -26,10 +27,13 @@ stdenvNoCC.mkDerivation {
installShellCompletion nixos-firewall-tool.{bash,fish}
'';
doCheck = buildPackages.shellcheck-minimal.compiler.bootstrapAvailable;
checkPhase = ''
shellcheck nixos-firewall-tool
'';
passthru.tests = {
shellcheck = runCommand "nixos-firewall-tool-shellcheck" { } ''
${lib.getExe buildPackages.shellcheck-minimal} ${finalAttrs.src}/nixos-firewall-tool
touch $out
'';
inherit (nixosTests) firewall firewall-nftables;
};
meta = {
description = "Tool to temporarily manipulate the NixOS firewall";
@@ -42,4 +46,4 @@ stdenvNoCC.mkDerivation {
platforms = lib.platforms.linux;
mainProgram = "nixos-firewall-tool";
};
}
})

View File

@@ -18,7 +18,7 @@
rustPlatform.buildRustPackage (finalAttrs: {
pname = "ty";
version = "0.0.95";
version = "0.0.96";
__structuredAttrs = true;
src = fetchFromGitHub {
@@ -26,7 +26,7 @@ rustPlatform.buildRustPackage (finalAttrs: {
repo = "ty";
tag = finalAttrs.version;
fetchSubmodules = true;
hash = "sha256-e0Yg6e50I4w9NDEaTUdoWFFvvJyDvL4eulcImy/HRRM=";
hash = "sha256-kjgmJZt8NH6fMppdAymBFWMWLKpTO3Pz1V6tzpndrhM=";
};
# For Darwin platforms, remove the integration test for file notifications,
@@ -40,7 +40,7 @@ rustPlatform.buildRustPackage (finalAttrs: {
cargoBuildFlags = [ "--package=ty" ];
cargoHash = "sha256-FaK/P6nBJqfSkUqv8qmmIeAPQh5ksIS/lpIBVkrGALg=";
cargoHash = "sha256-gl1X1CJto66I+dRyoqhebRdtTvh9C7o0LMwb+7T/+04=";
nativeBuildInputs = [ installShellFiles ];
buildInputs = [ rust-jemalloc-sys ];

View File

@@ -193,6 +193,7 @@ lib.makeOverridable (
inherit pname version src;
__structuredAttrs = true;
strictDeps = true;
enableParallelBuilding = true;

View File

@@ -244,6 +244,7 @@ lib.makeOverridable (
enableParallelBuilding = true;
__structuredAttrs = true;
strictDeps = true;
passthru = rec {
module = import ../../../../nixos/modules/system/boot/kernel_config.nix;