From b6c8359810152534c8e6bda471e17847d279ef7e Mon Sep 17 00:00:00 2001 From: Aliaksandr Date: Thu, 12 Feb 2026 02:14:41 +0200 Subject: [PATCH 01/31] bind: add json_c for JSON statistics channel support bind_exporter 0.8.0 defaults to the JSON statistics channel instead of XML. This requires BIND to be compiled with json-c support. Without this, BIND returns 404 for /json/v1/server requests, causing the prometheus-bind-exporter to fail to retrieve metrics. --- pkgs/by-name/bi/bind/package.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkgs/by-name/bi/bind/package.nix b/pkgs/by-name/bi/bind/package.nix index aa435fcb2feb..43a6c1ce1dcb 100644 --- a/pkgs/by-name/bi/bind/package.nix +++ b/pkgs/by-name/bi/bind/package.nix @@ -9,6 +9,7 @@ libidn2, libtool, libxml2, + json_c, openssl, liburcu, libuv, @@ -59,6 +60,7 @@ stdenv.mkDerivation (finalAttrs: { libidn2 libtool libxml2 + json_c openssl liburcu libuv From 95d571d0f5e203e6b7238c4251f2364a29c97765 Mon Sep 17 00:00:00 2001 From: Aliaksandr Date: Thu, 12 Feb 2026 02:14:47 +0200 Subject: [PATCH 02/31] prometheus-bind-exporter: 0.7.0 -> 0.8.0 https://github.com/prometheus-community/bind_exporter/releases/tag/v0.8.0 Changelog: - [CHANGE] Drop XML statistics v2 support - [CHANGE] Deprecate collection of task stats by default - [CHANGE] Update logging library - [ENHANCEMENT] Add metric rpz_rewrites - [BUGFIX] Make log level configurable via command-line flag The exporter now defaults to JSON statistics channel instead of XML. --- pkgs/by-name/pr/prometheus-bind-exporter/package.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/by-name/pr/prometheus-bind-exporter/package.nix b/pkgs/by-name/pr/prometheus-bind-exporter/package.nix index 1749028329e9..b135449f097b 100644 --- a/pkgs/by-name/pr/prometheus-bind-exporter/package.nix +++ b/pkgs/by-name/pr/prometheus-bind-exporter/package.nix @@ -7,16 +7,16 @@ buildGoModule rec { pname = "bind_exporter"; - version = "0.7.0"; + version = "0.8.0"; src = fetchFromGitHub { rev = "v${version}"; owner = "prometheus-community"; repo = "bind_exporter"; - sha256 = "sha256-x/XGatlXCKo9cI92JzFItApsjuZAfZX+8IZRpy7PVUo="; + sha256 = "sha256-r1P+zy3iMgPmfvIBgycW8KS0gfNOxCT9YMmHdeY4uXA="; }; - vendorHash = "sha256-f0ei/zotOj5ebURAOWUox/7J3jS2abQ5UgjninI9nRk="; + vendorHash = "sha256-/fPj5LOe3QdnVPdtYdaqtnGMJ7/SZ458mpvjwO8TxEI="; passthru.tests = { inherit (nixosTests.prometheus-exporters) bind; }; From d9a2e3b3c0ba8d4edf67475e1c64b00202cc58c6 Mon Sep 17 00:00:00 2001 From: Aliaksandr Date: Thu, 12 Feb 2026 02:14:53 +0200 Subject: [PATCH 03/31] nixos/prometheus-exporters/bind: update for bind_exporter 0.8.0 - Change default bindVersion from 'auto' to 'json' (new upstream default) - Add 'json' and 'xml' to bindVersion enum options - Remove deprecated 'xml.v2' option (dropped in 0.8.0) --- .../services/monitoring/prometheus/exporters/bind.nix | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/nixos/modules/services/monitoring/prometheus/exporters/bind.nix b/nixos/modules/services/monitoring/prometheus/exporters/bind.nix index 3390374172c4..935ebff7e5bd 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters/bind.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters/bind.nix @@ -17,7 +17,7 @@ in type = types.str; default = "http://localhost:8053/"; description = '' - HTTP XML API address of an Bind server. + HTTP API address of a BIND server. ''; }; bindTimeout = mkOption { @@ -29,13 +29,14 @@ in }; bindVersion = mkOption { type = types.enum [ - "xml.v2" + "json" + "xml" "xml.v3" "auto" ]; - default = "auto"; + default = "json"; description = '' - BIND statistics version. Can be detected automatically. + BIND statistics version. Defaults to JSON. ''; }; bindGroups = mkOption { From f9e4559b773822abfa1c3a8decc9377144aa3bb2 Mon Sep 17 00:00:00 2001 From: Aliaksandr Date: Thu, 12 Feb 2026 02:14:58 +0200 Subject: [PATCH 04/31] nixosTests.prometheus-exporters.bind: check bind_up metric Check for 'bind_up 1' instead of 'bind_query_recursions_total 0'. The bind_query_recursions_total metric is only emitted when there have been recursive queries. In a fresh BIND instance with no traffic, this counter doesn't exist. The bind_up metric reliably indicates that the exporter successfully connected to BIND's statistics channel. --- nixos/tests/prometheus-exporters.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix index 6383f1b54cfd..3f8d5fa61110 100644 --- a/nixos/tests/prometheus-exporters.nix +++ b/nixos/tests/prometheus-exporters.nix @@ -108,7 +108,7 @@ let wait_for_unit("prometheus-bind-exporter.service") wait_for_open_port(9119) succeed( - "curl -sSf http://localhost:9119/metrics | grep 'bind_query_recursions_total 0'" + "curl -sSf http://localhost:9119/metrics | grep 'bind_up 1'" ) ''; }; From bff3d1a971098e1a7e2ae1c7a1de31f7e04a1d66 Mon Sep 17 00:00:00 2001 From: Tom Herbers Date: Mon, 15 Jun 2026 21:38:42 +0200 Subject: [PATCH 05/31] libgit2: add withExperimentalSha256 argument --- pkgs/by-name/li/libgit2/package.nix | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkgs/by-name/li/libgit2/package.nix b/pkgs/by-name/li/libgit2/package.nix index 568fa09201aa..c17240378a92 100644 --- a/pkgs/by-name/li/libgit2/package.nix +++ b/pkgs/by-name/li/libgit2/package.nix @@ -17,6 +17,7 @@ gitstatus, llhttp, withGssapi ? false, + withExperimentalSha256 ? false, krb5, }: @@ -43,6 +44,7 @@ stdenv.mkDerivation (finalAttrs: { "-DUSE_HTTP_PARSER=llhttp" "-DUSE_SSH=ON" (lib.cmakeBool "USE_GSSAPI" withGssapi) + (lib.cmakeBool "EXPERIMENTAL_SHA256" withExperimentalSha256) "-DBUILD_SHARED_LIBS=${if staticBuild then "OFF" else "ON"}" ] ++ lib.optionals stdenv.hostPlatform.isWindows [ @@ -89,6 +91,13 @@ stdenv.mkDerivation (finalAttrs: { ) ''; + postInstall = lib.optionalString withExperimentalSha256 '' + # Downstream Rust bindings (git2-rs / git2-sys) expect experimental headers + # to be located at 'git2/experimental.h', but upstream libgit2 installs them + # into 'git2-experimental/' when EXPERIMENTAL_SHA256 is enabled. + ln -s git2-experimental $dev/include/git2 + ''; + passthru.tests = lib.mapAttrs (_: v: v.override { libgit2 = finalAttrs.finalPackage; }) { inherit libgit2-glib; inherit (python3Packages) pygit2; From 78067eb5942286a9ade63cdfc6c107c9710b563f Mon Sep 17 00:00:00 2001 From: Tom Herbers Date: Sun, 14 Jun 2026 12:18:42 +0200 Subject: [PATCH 06/31] gram: 2.1.2 -> 2.2.0 Changelog: https://codeberg.org/GramEditor/gram/releases/tag/2.2.0 --- pkgs/by-name/gr/gram/package.nix | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/by-name/gr/gram/package.nix b/pkgs/by-name/gr/gram/package.nix index 6d6f84306f33..48a4664be6fd 100644 --- a/pkgs/by-name/gr/gram/package.nix +++ b/pkgs/by-name/gr/gram/package.nix @@ -31,7 +31,7 @@ rustPlatform.buildRustPackage (finalAttrs: { pname = "gram"; - version = "2.1.2"; + version = "2.2.0"; outputs = [ "out" @@ -44,7 +44,7 @@ rustPlatform.buildRustPackage (finalAttrs: { owner = "GramEditor"; repo = "gram"; tag = finalAttrs.version; - hash = "sha256-7FzAvC/JMMIFcuTGkL2Ju644UAIsneOMhiDUFnQske4="; + hash = "sha256-w0uZ2qAc3Tt6QVRAX97LWW9aOs02fG1SEYCDhpUhinE="; }; postPatch = '' @@ -54,7 +54,7 @@ rustPlatform.buildRustPackage (finalAttrs: { --replace-fail '$CARGO_ABOUT_VERSION' '${cargo-about.version}' ''; - cargoHash = "sha256-feESY8ALSG3xa906HBc4pOKGerQ1jF7VUxzvUcsZbrY="; + cargoHash = "sha256-+lmDbawAIRllC7LzGJ9qPMtHXPd5aMoul47YOA7nfXA="; __structuredAttrs = true; @@ -71,7 +71,7 @@ rustPlatform.buildRustPackage (finalAttrs: { dontUseCmakeConfigure = true; buildInputs = [ - libgit2 + (libgit2.override { withExperimentalSha256 = true; }) openssl sqlite zlib From 2e23af70a8e4528205b3f45472bdb5007e4ab0bc Mon Sep 17 00:00:00 2001 From: Oskar Philipsson Date: Mon, 17 Nov 2025 08:51:40 +0100 Subject: [PATCH 07/31] nixos/systemd-initrd: add systemd.*.services.path to initrd store --- nixos/modules/system/boot/systemd/initrd.nix | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index f242cb8c6a5b..6a52b920c2e2 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -124,6 +124,16 @@ let jobScripts = concatLists ( mapAttrsToList (_: unit: unit.jobScripts or [ ]) (filterAttrs (_: v: v.enable) cfg.services) ); + unitEnv = pkgs.buildEnv { + name = "initrd-unit-env"; + paths = concatLists ( + mapAttrsToList (_: unit: unit.path or [ ]) (filterAttrs (_: v: v.enable) cfg.services) + ); + pathsToLink = [ + "/bin" + "/sbin" + ]; + }; stage1Units = generateUnits { type = "initrd"; @@ -636,6 +646,7 @@ in "${pkgs.bashNonInteractive}/bin" ] ++ jobScripts + ++ [ unitEnv ] ++ map (c: removeAttrs c [ "text" ]) (builtins.attrValues cfg.contents) ++ lib.optional (pkgs.stdenv.hostPlatform.libc == "glibc") "${pkgs.glibc}/lib/libnss_files.so.2"; From 494b92013764b1e576cdcc4231c31140c949adc6 Mon Sep 17 00:00:00 2001 From: Jost Alemann Date: Thu, 18 Jun 2026 21:17:37 +0200 Subject: [PATCH 08/31] ruff: 0.15.17 -> 0.15.18 Changelog: https://github.com/astral-sh/ruff/releases/tag/0.15.18 Diff: https://github.com/astral-sh/ruff/compare/0.15.17...0.15.18 --- pkgs/by-name/ru/ruff/package.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/by-name/ru/ruff/package.nix b/pkgs/by-name/ru/ruff/package.nix index 8aa8f51b8273..ca5d02475508 100644 --- a/pkgs/by-name/ru/ruff/package.nix +++ b/pkgs/by-name/ru/ruff/package.nix @@ -16,7 +16,7 @@ rustPlatform.buildRustPackage (finalAttrs: { pname = "ruff"; - version = "0.15.17"; + version = "0.15.18"; __structuredAttrs = true; @@ -24,12 +24,12 @@ rustPlatform.buildRustPackage (finalAttrs: { owner = "astral-sh"; repo = "ruff"; tag = finalAttrs.version; - hash = "sha256-+UsKRBe+lp/LdsmK/W11wCt2RypEryA5eBPb01OKCJw="; + hash = "sha256-DH00tENXdCdNcGPXPGzZsU3RVYQ0VBe1QLvbgEg/G6k="; }; cargoBuildFlags = [ "--package=ruff" ]; - cargoHash = "sha256-y1sKf+KXya/K+WUiIE357U6DXh/d+AQgj0SQIi1gpUw="; + cargoHash = "sha256-RmK14NMPbhoRVLwIF5GXdGQg2AnMH20JGx7XF4HO+wY="; nativeBuildInputs = [ installShellFiles ]; From bc7d3e5dda98e8bb1a813b817f327372cb26f5a1 Mon Sep 17 00:00:00 2001 From: Sergei Trofimovich Date: Thu, 18 Jun 2026 22:23:52 +0100 Subject: [PATCH 09/31] bind: 9.20.23 -> 9.20.24 Changes: https://bind9.readthedocs.io/en/bind-9.20/notes.html#notes-for-bind-9-20-24 --- pkgs/by-name/bi/bind/package.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/by-name/bi/bind/package.nix b/pkgs/by-name/bi/bind/package.nix index 2f12ebf30ea0..c47a0cb1433e 100644 --- a/pkgs/by-name/bi/bind/package.nix +++ b/pkgs/by-name/bi/bind/package.nix @@ -29,11 +29,11 @@ stdenv.mkDerivation (finalAttrs: { pname = "bind"; - version = "9.20.23"; + version = "9.20.24"; src = fetchurl { url = "https://downloads.isc.org/isc/bind9/${finalAttrs.version}/bind-${finalAttrs.version}.tar.xz"; - hash = "sha256-XUR1rtP55QDvVUsrFNlyvbg9M94hSps76SkY6kaQg3E="; + hash = "sha256-mJ/vH8iOpZ0EzYb4VNylpGFqIKmWi83ePBo2aKs2vgg="; }; outputs = [ From f35704a496985c19a04ca51e5963d354a78182b3 Mon Sep 17 00:00:00 2001 From: Jost Alemann Date: Thu, 18 Jun 2026 18:44:16 +0200 Subject: [PATCH 10/31] ty: 0.0.49 -> 0.0.51 Changelog: https://github.com/astral-sh/ty/releases/tag/0.0.51 Diff: https://github.com/astral-sh/ty/compare/0.0.49...0.0.51 --- pkgs/by-name/ty/ty/package.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/by-name/ty/ty/package.nix b/pkgs/by-name/ty/ty/package.nix index 41db756b4492..8a3b04bc7a09 100644 --- a/pkgs/by-name/ty/ty/package.nix +++ b/pkgs/by-name/ty/ty/package.nix @@ -17,7 +17,7 @@ rustPlatform.buildRustPackage (finalAttrs: { pname = "ty"; - version = "0.0.49"; + version = "0.0.51"; __structuredAttrs = true; src = fetchFromGitHub { @@ -25,7 +25,7 @@ rustPlatform.buildRustPackage (finalAttrs: { repo = "ty"; tag = finalAttrs.version; fetchSubmodules = true; - hash = "sha256-IKeoskueujGYFjhUd3V7iwKwZjFZqG3OYfe36S6J2aw="; + hash = "sha256-99CucCCZ8oOQV1PaPLZXq++8S7d6QJSAaQq/WXP87n4="; }; # For Darwin platforms, remove the integration test for file notifications, @@ -39,7 +39,7 @@ rustPlatform.buildRustPackage (finalAttrs: { cargoBuildFlags = [ "--package=ty" ]; - cargoHash = "sha256-y1sKf+KXya/K+WUiIE357U6DXh/d+AQgj0SQIi1gpUw="; + cargoHash = "sha256-N76A6howQn/ZbDJ7PWrThDYKhH+vc7tnZPShR7T31Ug="; nativeBuildInputs = [ installShellFiles ]; buildInputs = [ rust-jemalloc-sys ]; From d8b16de49ddea3c9fea3d9fe3c447cf0effd35fc Mon Sep 17 00:00:00 2001 From: Yureka Date: Fri, 19 Jun 2026 12:04:18 +0200 Subject: [PATCH 11/31] nixos: fix message on failure to determine mmap ASLR entropy Previously the script would stop early without any explanation. Now it continues to the point where the proper error message is printed, and then exits with an error exit status. --- nixos/modules/config/sysctl.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nixos/modules/config/sysctl.nix b/nixos/modules/config/sysctl.nix index df4165fbbec5..05f99aff70f4 100644 --- a/nixos/modules/config/sysctl.nix +++ b/nixos/modules/config/sysctl.nix @@ -74,7 +74,9 @@ in } ( '' + set +e mmap_rnd_bits_max=$(grep "^CONFIG_ARCH_MMAP_RND_BITS_MAX=" $configfile | grep --only-matching "[0-9]*$") + set -e if [[ -z "$mmap_rnd_bits_max" ]]; then echo "Unable to determine mmap_rnd_bits_max. Check your kernel configfile is valid." exit 1 @@ -83,7 +85,9 @@ in '' # HAVE_ARCH_MMAP_RND_COMPAT_BITS is not defined on 32-bit architectures or LoongArch64 + lib.optionalString (with pkgs.stdenv.hostPlatform; (!is32bit && !isLoongArch64)) '' + set +e mmap_rnd_compat_bits_max=$(grep "^CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=" $configfile | grep --only-matching "[0-9]*$") + set -e if [[ -z "$mmap_rnd_compat_bits_max" ]]; then echo "Unable to determine mmap_rnd_compat_bits_max. Check your kernel configfile is valid." exit 1 From ad3e95b0931c8c618dc3fd017174fd97302c5538 Mon Sep 17 00:00:00 2001 From: Ilan Joselevich Date: Fri, 19 Jun 2026 12:36:40 +0300 Subject: [PATCH 12/31] switch-to-configuration-ng: handle socket-activated Accept=yes services A per-connection instance spawned by an Accept=yes socket (e.g. foo@1234.service) has an implicit socket of foo.socket, but the implicit name was derived from the template base name including the trailing "@", yielding foo@.socket. That never matches an active unit, so such running instances were treated as non-socket-activated and the activation script tried to start them directly on a configuration switch. That fails because there is no connection socket to pass, sending the instance into a restart loop. Strip the trailing "@" so the implicit socket resolves correctly; the running instances are then stopped and their socket restarted, leaving systemd to spawn fresh instances for new connections. Resolves: https://github.com/NixOS/nixpkgs/issues/533205 Assisted-by: Claude:claude-opus-4-8 --- nixos/tests/switch-test.nix | 64 +++++++++++++++++++ .../sw/switch-to-configuration-ng/src/main.rs | 6 +- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/nixos/tests/switch-test.nix b/nixos/tests/switch-test.nix index 2dbc9386f8ae..63dc78e2e6dc 100644 --- a/nixos/tests/switch-test.nix +++ b/nixos/tests/switch-test.nix @@ -43,6 +43,19 @@ let server.serve_forever() ''; + # Per-connection (Accept=yes) socket-activated service that requires the + # connection socket to be passed via socket activation and fails when started + # without one. It greets the client and stays alive for as long as the + # connection is held open. + acceptSocketTest = pkgs.writeShellScript "accept-socket-test.sh" '' + if [ "''${LISTEN_FDS:-0}" -lt 1 ]; then + echo "Expected exactly one socket, got 0" >&2 + exit 4 + fi + printf hello >&3 + exec ${lib.getExe' pkgs.coreutils "cat"} <&3 >/dev/null + ''; + in { name = "switch-test"; @@ -508,6 +521,26 @@ in }; }; + accept-socket.configuration = { + systemd.sockets.accept-socket = { + wantedBy = [ "sockets.target" ]; + listenStreams = [ "/run/accept-test.sock" ]; + socketConfig = { + Accept = "yes"; + SocketMode = "0777"; + }; + }; + systemd.services."accept-socket@" = { + description = "A per-connection socket-activated service"; + serviceConfig.ExecStart = acceptSocketTest; + }; + }; + + accept-socket-service-modified.configuration = { + imports = [ accept-socket.configuration ]; + systemd.services."accept-socket@".serviceConfig.X-Test = "test"; + }; + mount.configuration = { systemd.mounts = [ { @@ -1587,6 +1620,37 @@ in if machine.succeed("socat - UNIX-CONNECT:/run/test.sock") != "hello": raise Exception("Socket was not properly activated after the service was restarted") + with subtest("socket-activated services with Accept=yes"): + # Socket-activated services don't get started, just the socket + machine.fail("[ -S /run/accept-test.sock ]") + out = switch_to_specialisation("${machine}", "accept-socket") + assert_contains(out, "the following new units were started: accept-socket.socket\n") + machine.succeed("[ -S /run/accept-test.sock ]") + + # Hold a connection open so a per-connection instance keeps running + machine.succeed("socat EXEC:'sleep infinity' UNIX-CONNECT:/run/accept-test.sock >&2 &") + instance = machine.wait_until_succeeds( + "systemctl list-units --no-legend --state=running 'accept-socket@*.service' " + + "| grep -m1 -o 'accept-socket@[^ ]*\\.service'" + ).strip() + + # Changing the templated service must stop the running instance and + # restart the socket instead of (re)starting the per-connection + # instance, which cannot be started without a connection socket + out = switch_to_specialisation("${machine}", "accept-socket-service-modified") + assert_contains(out, "stopping the following units:") + assert_contains(out, instance) + assert_contains(out, "accept-socket.socket") + assert_contains(out, "\nstarting the following units: accept-socket.socket\n") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "\nrestarting the following units:") + # The per-connection instance must not be (re)started + starting = out[out.index("\nstarting the following units:") :] + assert instance not in starting, f"instance {instance} should not be (re)started" + # Socket-activation of the unit still works + if machine.succeed("socat - UNIX-CONNECT:/run/accept-test.sock Date: Fri, 19 Jun 2026 12:54:08 -0700 Subject: [PATCH 13/31] mdadm: 4.4 -> 4.6 Changelog: https://git.kernel.org/pub/scm/utils/mdadm/mdadm.git/tree/CHANGELOG.md?h=mdadm-4.6 --- .../md/mdadm4/no-self-references.patch | 124 ------------------ pkgs/by-name/md/mdadm4/package.nix | 16 +-- 2 files changed, 3 insertions(+), 137 deletions(-) delete mode 100644 pkgs/by-name/md/mdadm4/no-self-references.patch diff --git a/pkgs/by-name/md/mdadm4/no-self-references.patch b/pkgs/by-name/md/mdadm4/no-self-references.patch deleted file mode 100644 index 3b3dc4d84609..000000000000 --- a/pkgs/by-name/md/mdadm4/no-self-references.patch +++ /dev/null @@ -1,124 +0,0 @@ -diff --git a/Makefile b/Makefile -index 2a51d813..a31ac48a 100644 ---- a/Makefile -+++ b/Makefile -@@ -63,6 +63,9 @@ endif - ifdef DEBIAN - CPPFLAGS += -DDEBIAN - endif -+ifdef NIXOS -+CPPFLAGS += -DNIXOS -+endif - ifdef DEFAULT_OLD_METADATA - CPPFLAGS += -DDEFAULT_OLD_METADATA - DEFAULT_METADATA=0.90 -@@ -129,6 +132,7 @@ endif - INSTALL = /usr/bin/install - DESTDIR = - BINDIR = /sbin -+INSTALL_BINDIR = ${BINDIR} - MANDIR = /usr/share/man - MAN4DIR = $(MANDIR)/man4 - MAN5DIR = $(MANDIR)/man5 -@@ -253,16 +257,16 @@ sha1.o : sha1.c sha1.h md5.h - install : install-bin install-man install-udev - - install-static : mdadm.static install-man -- $(INSTALL) -D $(STRIP) -m 755 mdadm.static $(DESTDIR)$(BINDIR)/mdadm -+ $(INSTALL) -D $(STRIP) -m 755 mdadm.static $(DESTDIR)$(INSTALL_BINDIR)/mdadm - - install-tcc : mdadm.tcc install-man -- $(INSTALL) -D $(STRIP) -m 755 mdadm.tcc $(DESTDIR)$(BINDIR)/mdadm -+ $(INSTALL) -D $(STRIP) -m 755 mdadm.tcc $(DESTDIR)$(INSTALL_BINDIR)/mdadm - - install-uclibc : mdadm.uclibc install-man -- $(INSTALL) -D $(STRIP) -m 755 mdadm.uclibc $(DESTDIR)$(BINDIR)/mdadm -+ $(INSTALL) -D $(STRIP) -m 755 mdadm.uclibc $(DESTDIR)$(INSTALL_BINDIR)/mdadm - - install-klibc : mdadm.klibc install-man -- $(INSTALL) -D $(STRIP) -m 755 mdadm.klibc $(DESTDIR)$(BINDIR)/mdadm -+ $(INSTALL) -D $(STRIP) -m 755 mdadm.klibc $(DESTDIR)$(INSTALL_BINDIR)/mdadm - - install-man: mdadm.8 md.4 mdadm.conf.5 mdmon.8 - $(INSTALL) -D -m 644 mdadm.8 $(DESTDIR)$(MAN8DIR)/mdadm.8 -@@ -305,7 +309,7 @@ install-bin: mdadm mdmon - $(INSTALL) -D $(STRIP) -m 755 mdmon $(DESTDIR)$(BINDIR)/mdmon - - uninstall: -- rm -f $(DESTDIR)$(MAN8DIR)/mdadm.8 $(DESTDIR)$(MAN8DIR)/mdmon.8 $(DESTDIR)$(MAN4DIR)/md.4 $(DESTDIR)$(MAN5DIR)/mdadm.conf.5 $(DESTDIR)$(BINDIR)/mdadm -+ rm -f $(DESTDIR)$(MAN8DIR)/mdadm.8 $(DESTDIR)$(MAN8DIR)/mdmon.8 $(DESTDIR)$(MAN4DIR)/md.4 $(DESTDIR)$(MAN5DIR)/mdadm.conf.5 $(DESTDIR)$(INSTALL_BINDIR)/mdadm - - test: mdadm mdmon test_stripe swap_super raid6check - @echo "Please run './test' as root" -diff --git a/policy.c b/policy.c -index eee9ef63..9f916e9d 100644 ---- a/policy.c -+++ b/policy.c -@@ -817,12 +817,39 @@ char *find_rule(struct rule *rule, char *rule_type) - #define UDEV_RULE_FORMAT \ - "ACTION==\"add\", SUBSYSTEM==\"block\", " \ - "ENV{DEVTYPE}==\"%s\", ENV{ID_PATH}==\"%s\", " \ --"RUN+=\"" BINDIR "/mdadm --incremental $env{DEVNAME}\"\n" -+"RUN+=\"%s/mdadm --incremental $env{DEVNAME}\"\n" - - #define UDEV_RULE_FORMAT_NOTYPE \ - "ACTION==\"add\", SUBSYSTEM==\"block\", " \ - "ENV{ID_PATH}==\"%s\", " \ --"RUN+=\"" BINDIR "/mdadm --incremental $env{DEVNAME}\"\n" -+"RUN+=\"%s/mdadm --incremental $env{DEVNAME}\"\n" -+ -+#ifdef NIXOS -+const char *get_mdadm_bindir(void) -+{ -+ static char *bindir = NULL; -+ if (bindir != NULL) { -+ return bindir; -+ } else { -+ int len; -+ bindir = xmalloc(1025); -+ len = readlink("/proc/self/exe", bindir, 1024); -+ if (len > 0) { -+ char *basename; -+ if ((basename = strrchr(bindir, '/')) != NULL) -+ *basename = '\0'; -+ else -+ *(bindir + len) = '\0'; -+ } else { -+ *bindir = '\0'; -+ } -+ return bindir; -+ } -+} -+#define SELF get_mdadm_bindir() -+#else -+#define SELF BINDIR -+#endif - - /* Write rule in the rule file. Use format from UDEV_RULE_FORMAT */ - int write_rule(struct rule *rule, int fd, int force_part) -@@ -836,9 +863,9 @@ int write_rule(struct rule *rule, int fd, int force_part) - if (force_part) - typ = type_part; - if (typ) -- snprintf(line, sizeof(line) - 1, UDEV_RULE_FORMAT, typ, pth); -+ snprintf(line, sizeof(line) - 1, UDEV_RULE_FORMAT, typ, pth, SELF); - else -- snprintf(line, sizeof(line) - 1, UDEV_RULE_FORMAT_NOTYPE, pth); -+ snprintf(line, sizeof(line) - 1, UDEV_RULE_FORMAT_NOTYPE, pth, SELF); - return write(fd, line, strlen(line)) == (int)strlen(line); - } - -diff --git a/util.c b/util.c -index 3d05d074..e004a798 100644 ---- a/util.c -+++ b/util.c -@@ -1913,7 +1913,9 @@ int start_mdmon(char *devnm) - char pathbuf[1024]; - char *paths[4] = { - pathbuf, -+#ifndef NIXOS - BINDIR "/mdmon", -+#endif - "./mdmon", - NULL - }; diff --git a/pkgs/by-name/md/mdadm4/package.nix b/pkgs/by-name/md/mdadm4/package.nix index 2143acc494e5..a35b504f6234 100644 --- a/pkgs/by-name/md/mdadm4/package.nix +++ b/pkgs/by-name/md/mdadm4/package.nix @@ -15,27 +15,16 @@ stdenv.mkDerivation (finalAttrs: { pname = "mdadm"; - version = "4.4"; + version = "4.6"; src = fetchgit { url = "https://git.kernel.org/pub/scm/utils/mdadm/mdadm.git"; tag = "mdadm-${finalAttrs.version}"; - hash = "sha256-jGmc8fkJM0V9J7V7tQPXSF/WD0kzyEAloBAwaAFenS0="; + hash = "sha256-jFsVPJC4lcShkSwQCGjVdVkvk4q4weM7i5DzrLgpuSM="; }; patches = [ - ./no-self-references.patch ./fix-hardcoded-mapdir.patch - # Fixes build on musl - (fetchurl { - url = "https://raw.githubusercontent.com/void-linux/void-packages/e58d2b17d3c40faffc0d426aab00184f28d9dafa/srcpkgs/mdadm/patches/musl.patch"; - hash = "sha256-TIcQs+8RM5Q6Z8MHkI50kaJd7f9WdS/EVI16F7b2+SA="; - }) - # Fixes build on musl 1.2.5+ - (fetchurl { - url = "https://lore.kernel.org/linux-raid/20240220165158.3521874-1-raj.khem@gmail.com/raw"; - hash = "sha256-JOZ8n7zi+nq236NPpB4e2gUy8I3l3DbcoLhpeL73f98="; - }) ]; makeFlags = [ @@ -97,6 +86,7 @@ stdenv.mkDerivation (finalAttrs: { }; meta = { + changelog = "https://git.kernel.org/pub/scm/utils/mdadm/mdadm.git/tree/CHANGELOG.md?h=${finalAttrs.src.tag}"; description = "Programs for managing RAID arrays under Linux"; homepage = "https://git.kernel.org/pub/scm/utils/mdadm/mdadm.git"; license = lib.licenses.gpl2Plus; From ef352e5401b276b155a93cba39e6a02ac2d7ebfc Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Mon, 1 Jun 2026 19:54:38 +0200 Subject: [PATCH 14/31] nixos/fuse: disable by default This change disables the fuse module by default and shifts the obligation to enable it to consumers. --- nixos/doc/manual/release-notes/rl-2611.section.md | 2 ++ nixos/modules/profiles/bashless.nix | 2 -- nixos/modules/programs/fuse.nix | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2611.section.md b/nixos/doc/manual/release-notes/rl-2611.section.md index 0252bcb370ea..2a79a0500d3a 100644 --- a/nixos/doc/manual/release-notes/rl-2611.section.md +++ b/nixos/doc/manual/release-notes/rl-2611.section.md @@ -58,4 +58,6 @@ - `security.polkit.settings` added for RFC42 style configuration of the polkitd daemon. +- The `programs.fuse` module, which provides the `fusermount3` executable and the `/etc/fuse.conf` config file, is now opt-in. The obligation to enable it has been shifted to its various consumers (e.g. gvfs, flatpak, appimage, sshfs). This can break fuse consumers at runtime, that don't explicitly declare that dependency with a module, e.g the mounting functionality in various backup tools (borg, restic, rclone, ...). + - The `newuidmap` and `newgidmap` security wrappers are now installed with `cap_setuid`/`cap_setgid` file capabilities instead of the setuid-root bit, matching shadow's `--with-fcaps` install mode and other major distributions. Rootless containers (podman, docker-rootless, unprivileged user namespaces) are unaffected. The only behavioural change is that mapping host uid 0 via `/etc/subuid` (which NixOS never configures by default) additionally requires `cap_setfcap`; users who explicitly grant uid 0 in a subuid range can restore the previous behaviour with `security.wrappers.newuidmap.capabilities = lib.mkForce "cap_setuid,cap_setfcap+ep";`. diff --git a/nixos/modules/profiles/bashless.nix b/nixos/modules/profiles/bashless.nix index 501563b97b3d..20f2f7256f07 100644 --- a/nixos/modules/profiles/bashless.nix +++ b/nixos/modules/profiles/bashless.nix @@ -15,8 +15,6 @@ environment.corePackages = lib.mkForce [ ]; # Contains bash completions nix.enable = lib.mkDefault false; - # The fuse{,3} package contains a runtime dependency on bash. - programs.fuse.enable = lib.mkDefault false; documentation.man.man-db.enable = lib.mkDefault false; # autovt depends on bash console.enable = lib.mkDefault false; diff --git a/nixos/modules/programs/fuse.nix b/nixos/modules/programs/fuse.nix index e7e317d7ef9b..07026cbd33db 100644 --- a/nixos/modules/programs/fuse.nix +++ b/nixos/modules/programs/fuse.nix @@ -12,9 +12,7 @@ in meta.maintainers = [ ]; options.programs.fuse = { - enable = lib.mkEnableOption "fuse" // { - default = true; - }; + enable = lib.mkEnableOption "fuse"; mountMax = lib.mkOption { # In the C code it's an "int" (i.e. signed and at least 16 bit), but From 5a7a109c5ac4e41c5d508ddfd45129cd9d1f1319 Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Mon, 1 Jun 2026 19:56:50 +0200 Subject: [PATCH 15/31] nixos/gvfs: enable fuse --- nixos/modules/services/desktops/gvfs.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nixos/modules/services/desktops/gvfs.nix b/nixos/modules/services/desktops/gvfs.nix index 004810327798..cebfb0820229 100644 --- a/nixos/modules/services/desktops/gvfs.nix +++ b/nixos/modules/services/desktops/gvfs.nix @@ -40,6 +40,8 @@ in environment.systemPackages = [ cfg.package ]; + programs.fuse.enable = true; + services.dbus.packages = [ cfg.package ]; systemd.packages = [ cfg.package ]; From bd44b6318872853768665494351ad5fe75e83f0d Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Tue, 2 Jun 2026 01:43:26 +0200 Subject: [PATCH 16/31] nixos/kubo: enable programs.fuse --- nixos/modules/services/network-filesystems/kubo.nix | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nixos/modules/services/network-filesystems/kubo.nix b/nixos/modules/services/network-filesystems/kubo.nix index 3c75423f3ffa..4e676b80a1aa 100644 --- a/nixos/modules/services/network-filesystems/kubo.nix +++ b/nixos/modules/services/network-filesystems/kubo.nix @@ -332,8 +332,9 @@ in boot.kernel.sysctl."net.core.rmem_max" = lib.mkDefault 7500000; boot.kernel.sysctl."net.core.wmem_max" = lib.mkDefault 7500000; - programs.fuse = lib.mkIf (cfg.autoMount && cfg.settings.Mounts.FuseAllowOther) { - userAllowOther = true; + programs.fuse = { + enable = lib.mkIf cfg.autoMount true; + userAllowOther = lib.mkIf cfg.settings.Mounts.fuseAllowOther true; }; users.users = lib.mkIf (cfg.user == "ipfs") { From 69a96a84d616f8075fb03c4c233d11ee994cc2d8 Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Tue, 2 Jun 2026 02:05:22 +0200 Subject: [PATCH 17/31] nixos/flatpak: enable fuse --- nixos/modules/services/desktops/flatpak.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nixos/modules/services/desktops/flatpak.nix b/nixos/modules/services/desktops/flatpak.nix index 5756738c9fda..e874cbac7a4f 100644 --- a/nixos/modules/services/desktops/flatpak.nix +++ b/nixos/modules/services/desktops/flatpak.nix @@ -40,6 +40,8 @@ in pkgs.fuse3 ]; + programs.fuse.enable = true; + security.polkit.enable = true; fonts.fontDir.enable = true; From 28c15019478ea63cb4484f6e819993d9a43f6fb4 Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Tue, 2 Jun 2026 02:06:30 +0200 Subject: [PATCH 18/31] nixos/appimage: enable fuse --- nixos/modules/programs/appimage.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nixos/modules/programs/appimage.nix b/nixos/modules/programs/appimage.nix index c0379557c97a..4662a6c3d8cc 100644 --- a/nixos/modules/programs/appimage.nix +++ b/nixos/modules/programs/appimage.nix @@ -43,6 +43,8 @@ in } ); environment.systemPackages = [ cfg.package ]; + + programs.fuse.enable = true; }; meta.maintainers = with lib.maintainers; [ From ab51f0dbef1ff1b7ba0fc1b59b3a366117eaefd2 Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Tue, 2 Jun 2026 02:43:40 +0200 Subject: [PATCH 19/31] nixos/sshfs: enable fuse --- nixos/modules/tasks/filesystems/sshfs.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nixos/modules/tasks/filesystems/sshfs.nix b/nixos/modules/tasks/filesystems/sshfs.nix index f070779ecc54..917ceea7b691 100644 --- a/nixos/modules/tasks/filesystems/sshfs.nix +++ b/nixos/modules/tasks/filesystems/sshfs.nix @@ -10,6 +10,8 @@ lib.mkIf (config.boot.supportedFilesystems.sshfs or config.boot.supportedFilesystems."fuse.sshfs" or false) { + programs.fuse.enable = true; + system.fsPackages = [ pkgs.sshfs ]; }; } From 99ed707074f5a579998e1072a3b83fd9f9d16e97 Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Fri, 5 Jun 2026 13:56:54 +0200 Subject: [PATCH 20/31] nixos/test/gocryptfs: enable fuse --- nixos/tests/gocryptfs.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nixos/tests/gocryptfs.nix b/nixos/tests/gocryptfs.nix index d0e5df5e40b5..763e1c1407fa 100644 --- a/nixos/tests/gocryptfs.nix +++ b/nixos/tests/gocryptfs.nix @@ -13,6 +13,8 @@ pkgs.openssl ]; + programs.fuse.enable = true; + specialisation.fstab-test.configuration = { # This can't be fileSytems, as the qemu machinery doesn't honor it. virtualisation.fileSystems."/plain" = { From a92efa870c547481956055a2011475aee39b1ae1 Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Mon, 15 Jun 2026 17:51:15 +0200 Subject: [PATCH 21/31] nixos/plasma6: opt into fuse Used by the kio-* stack. --- nixos/modules/services/desktop-managers/plasma6.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/nixos/modules/services/desktop-managers/plasma6.nix b/nixos/modules/services/desktop-managers/plasma6.nix index 0813b1b6b84c..f6106d3e83fe 100644 --- a/nixos/modules/services/desktop-managers/plasma6.nix +++ b/nixos/modules/services/desktop-managers/plasma6.nix @@ -274,6 +274,7 @@ in services.power-profiles-daemon.enable = mkDefault true; services.system-config-printer.enable = mkIf config.services.printing.enable (mkDefault true); + programs.fuse.enable = true; services.udisks2.enable = true; services.upower.enable = config.powerManagement.enable; services.libinput.enable = mkDefault true; From 944a258aebf337420c2aaf0332228682b2dad956 Mon Sep 17 00:00:00 2001 From: Ilan Joselevich Date: Fri, 19 Jun 2026 15:14:40 +0300 Subject: [PATCH 22/31] switch-to-configuration-ng: handle transition to socket activation When a service gains a `.socket` unit, the socket is not yet active, so the `active_cur.contains_key(socket)` check is false and the service is queued in `units_to_start`. That races with the socket unit being started by `sockets.target`, producing "Socket service already active, refusing" or "no socket activation file descriptors found" errors. Treat the service as socket-activated whenever the socket is absent now but present in the new configuration, leaving it to be activated on demand by the newly started socket rather than started directly. Fixes: https://github.com/NixOS/nixpkgs/pull/510391#issuecomment-4273511176 Assisted-by: Claude:claude-opus-4-8 --- nixos/tests/switch-test.nix | 18 +++++++++++++++++ .../sw/switch-to-configuration-ng/src/main.rs | 20 +++++++++++++------ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/nixos/tests/switch-test.nix b/nixos/tests/switch-test.nix index 63dc78e2e6dc..b81d84086f2e 100644 --- a/nixos/tests/switch-test.nix +++ b/nixos/tests/switch-test.nix @@ -541,6 +541,12 @@ in systemd.services."accept-socket@".serviceConfig.X-Test = "test"; }; + socket-activated-without-socket.configuration = { + imports = [ simple-socket.configuration ]; + systemd.sockets.socket-activated.enable = false; + systemd.services.socket-activated.wantedBy = [ "multi-user.target" ]; + }; + mount.configuration = { systemd.mounts = [ { @@ -1620,6 +1626,18 @@ in if machine.succeed("socat - UNIX-CONNECT:/run/test.sock") != "hello": raise Exception("Socket was not properly activated after the service was restarted") + # A service transitioning to socket activation is not started directly, + # it's left for the newly started socket to activate on demand + switch_to_specialisation("${machine}", "socket-activated-without-socket") + machine.succeed("systemctl is-active socket-activated.service") + out = switch_to_specialisation("${machine}", "simple-socket-stop-if-changed") + assert_contains(out, "stopping the following units: socket-activated.service\n") + assert_lacks(out, "\nstarting the following units:") + assert_contains(out, "the following new units were started: socket-activated.socket\n") + machine.succeed("[ -S /run/test.sock ]") + if machine.succeed("socat - UNIX-CONNECT:/run/test.sock") != "hello": + raise Exception("Socket was not properly activated after the transition") + with subtest("socket-activated services with Accept=yes"): # Socket-activated services don't get started, just the socket machine.fail("[ -S /run/accept-test.sock ]") diff --git a/pkgs/by-name/sw/switch-to-configuration-ng/src/main.rs b/pkgs/by-name/sw/switch-to-configuration-ng/src/main.rs index ec6bf1ad253f..b5d307d7bdb1 100644 --- a/pkgs/by-name/sw/switch-to-configuration-ng/src/main.rs +++ b/pkgs/by-name/sw/switch-to-configuration-ng/src/main.rs @@ -777,6 +777,9 @@ fn handle_modified_unit( } for socket in &sockets { + let socket_in_new_config = + toplevel.join(scope.etc_dir()).join(socket).exists(); + if active_cur.contains_key(socket) { // We can now be sure this is a socket-activated unit @@ -787,7 +790,7 @@ fn handle_modified_unit( } // Only restart sockets that actually exist in new configuration: - if toplevel.join(scope.etc_dir()).join(socket).exists() { + if socket_in_new_config { if use_restart_as_stop_and_start { units_to_restart.insert(socket.to_string(), ()); record_unit(&restart_list, socket); @@ -798,12 +801,17 @@ fn handle_modified_unit( socket_activated = true; } + } else if socket_in_new_config { + // Transitioning to socket activation; let the socket start it. + socket_activated = true; + } else { + continue; + } - // Remove from units to reload so we don't restart and reload - if units_to_reload.contains_key(unit) { - units_to_reload.remove(unit); - unrecord_unit(&reload_list, unit); - } + // Remove from units to reload so we don't restart and reload + if units_to_reload.contains_key(unit) { + units_to_reload.remove(unit); + unrecord_unit(&reload_list, unit); } } } From be9a0ac9924a5c4f116f7f4bc18be335664272e9 Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Sat, 20 Jun 2026 20:18:30 +0200 Subject: [PATCH 23/31] nixos/test-driver: properly tokenize udev rule When running a container test the udev linter complains, that tokens should be separate by a comma followed by a space. --- nixos/lib/testing/network.nix | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/nixos/lib/testing/network.nix b/nixos/lib/testing/network.nix index 570b854d8e28..86d65faf21ad 100644 --- a/nixos/lib/testing/network.nix +++ b/nixos/lib/testing/network.nix @@ -135,8 +135,13 @@ let ); udevRules = map ( interface: - # MAC Addresses for QEMU network devices are lowercase, and udev string comparison is case-sensitive. - ''SUBSYSTEM=="net",ACTION=="add",ATTR{address}=="${toLower (qemu-common.qemuNicMac interface.vlan config.virtualisation.test.nodeNumber)}",NAME="${interface.name}"'' + lib.concatStringsSep ", " [ + ''SUBSYSTEM=="net"'' + ''ACTION=="add"'' + # MAC Addresses for QEMU network devices are lowercase, and udev string comparison is case-sensitive. + ''ATTR{address}=="${toLower (qemu-common.qemuNicMac interface.vlan config.virtualisation.test.nodeNumber)}"'' + ''NAME="${interface.name}"'' + ] ) interfaces; in { From 7106a694391b7839c3d9c0a3c4eb0eb5e731338a Mon Sep 17 00:00:00 2001 From: Jost Alemann Date: Tue, 23 Jun 2026 04:21:47 +0200 Subject: [PATCH 24/31] ty: 0.0.51 -> 0.0.52 Changelog: https://github.com/astral-sh/ty/releases/tag/0.0.52 Diff: https://github.com/astral-sh/ty/compare/0.0.51...0.0.52 --- pkgs/by-name/ty/ty/package.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/by-name/ty/ty/package.nix b/pkgs/by-name/ty/ty/package.nix index 8a3b04bc7a09..48a4dbae05f2 100644 --- a/pkgs/by-name/ty/ty/package.nix +++ b/pkgs/by-name/ty/ty/package.nix @@ -17,7 +17,7 @@ rustPlatform.buildRustPackage (finalAttrs: { pname = "ty"; - version = "0.0.51"; + version = "0.0.52"; __structuredAttrs = true; src = fetchFromGitHub { @@ -25,7 +25,7 @@ rustPlatform.buildRustPackage (finalAttrs: { repo = "ty"; tag = finalAttrs.version; fetchSubmodules = true; - hash = "sha256-99CucCCZ8oOQV1PaPLZXq++8S7d6QJSAaQq/WXP87n4="; + hash = "sha256-p9fTzQ4DYvnwrtLdpSTekBV4ZbR1KETR8dfsbp8CDpo="; }; # For Darwin platforms, remove the integration test for file notifications, @@ -39,7 +39,7 @@ rustPlatform.buildRustPackage (finalAttrs: { cargoBuildFlags = [ "--package=ty" ]; - cargoHash = "sha256-N76A6howQn/ZbDJ7PWrThDYKhH+vc7tnZPShR7T31Ug="; + cargoHash = "sha256-NUIdYOeyRsR/ZQueEXshYdWTnSeQiRjZRRi2ag8Dm48="; nativeBuildInputs = [ installShellFiles ]; buildInputs = [ rust-jemalloc-sys ]; From cac02061f84ad05a4d8db7c6f245fbe21b70f4c2 Mon Sep 17 00:00:00 2001 From: r-vdp Date: Tue, 23 Jun 2026 14:21:17 +0200 Subject: [PATCH 25/31] switch-to-configuration-ng: pull common code out of the different conditional branches All branches here are restarting instead of reloading, so it makes sense to remove the unit from units_to_reload in all cases. --- .../sw/switch-to-configuration-ng/src/main.rs | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/pkgs/by-name/sw/switch-to-configuration-ng/src/main.rs b/pkgs/by-name/sw/switch-to-configuration-ng/src/main.rs index b5d307d7bdb1..55d1d93bde34 100644 --- a/pkgs/by-name/sw/switch-to-configuration-ng/src/main.rs +++ b/pkgs/by-name/sw/switch-to-configuration-ng/src/main.rs @@ -743,11 +743,6 @@ fn handle_modified_unit( // This unit should be restarted instead of stopped and started. units_to_restart.insert(unit.to_string(), ()); record_unit(&restart_list, unit); - // Remove from units to reload so we don't restart and reload - if units_to_reload.contains_key(unit) { - units_to_reload.remove(unit); - unrecord_unit(&reload_list, unit); - } } else { // If this unit is socket-activated, then stop the socket unit(s) as well, and // restart the socket(s) instead of the service. @@ -804,14 +799,6 @@ fn handle_modified_unit( } else if socket_in_new_config { // Transitioning to socket activation; let the socket start it. socket_activated = true; - } else { - continue; - } - - // Remove from units to reload so we don't restart and reload - if units_to_reload.contains_key(unit) { - units_to_reload.remove(unit); - unrecord_unit(&reload_list, unit); } } } @@ -840,11 +827,11 @@ fn handle_modified_unit( } else { units_to_stop.insert(unit.to_string(), ()); } - // Remove from units to reload so we don't restart and reload - if units_to_reload.contains_key(unit) { - units_to_reload.remove(unit); - unrecord_unit(&reload_list, unit); - } + } + + // Remove from units to reload so we don't restart and reload + if units_to_reload.remove(unit).is_some() { + unrecord_unit(&reload_list, unit); } } } From 7601f8a10854992b594fc85ecb4685a1a0ec3346 Mon Sep 17 00:00:00 2001 From: Yuriy Taraday Date: Tue, 23 Jun 2026 15:42:14 +0200 Subject: [PATCH 26/31] pkgs-lib/formats: Add tests for serializing null values In the effort to pass values directly as structured values, we ran into issue with serializing null's. This adds tests for null for all formats that support it and negative tests for some others. See https://github.com/NixOS/nixpkgs/pull/524404#issuecomment-4665694636 --- pkgs/pkgs-lib/formats.nix | 8 +++- pkgs/pkgs-lib/tests/formats.nix | 83 +++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/pkgs/pkgs-lib/formats.nix b/pkgs/pkgs-lib/formats.nix index ef7d104f20e4..a184b0cbce53 100644 --- a/pkgs/pkgs-lib/formats.nix +++ b/pkgs/pkgs-lib/formats.nix @@ -1017,7 +1017,13 @@ optionalAttrs allowAliases aliases }: if format == "badgerfish" then { - type = serializableValueWith { typeName = "XML"; }; + type = + attrsOf (serializableValueWith { + typeName = "XML"; + }) + // { + description = "XML value"; + }; generate = name: value: diff --git a/pkgs/pkgs-lib/tests/formats.nix b/pkgs/pkgs-lib/tests/formats.nix index ee6d817287c2..242fd64bc70a 100644 --- a/pkgs/pkgs-lib/tests/formats.nix +++ b/pkgs/pkgs-lib/tests/formats.nix @@ -142,6 +142,14 @@ runBuildTests { ''; }; + jsonNull = shouldPass { + format = formats.json { }; + input = null; + expected = '' + null + ''; + }; + yaml_1_1Atoms = shouldPass { format = formats.yaml_1_1 { }; input = { @@ -176,6 +184,15 @@ runBuildTests { ''; }; + yaml_1_1Null = shouldPass { + format = formats.yaml_1_1 { }; + input = null; + expected = '' + null + ... + ''; + }; + yaml_1_2Atoms = shouldPass { format = formats.yaml_1_2 { }; input = { @@ -210,6 +227,16 @@ runBuildTests { ''; }; + yaml_1_2Null = shouldPass { + format = formats.yaml_1_2 { }; + input = null; + # nixfmt insists on removing indentation, so force it with ${" "} + expected = '' + + ${" "}null + ''; + }; + iniAtoms = shouldPass { format = formats.ini { }; input = { @@ -879,6 +906,14 @@ runBuildTests { ''; }; + cdnNull = shouldPass { + format = formats.cdn { }; + input = null; + expected = '' + null: null + ''; + }; + # This test is responsible for # 1. testing type coercions # 2. providing a more readable example test @@ -989,6 +1024,14 @@ runBuildTests { ''; }; + luaNull = shouldPass { + format = formats.lua { }; + input = null; + expected = '' + return nil + ''; + }; + nixConfAtoms = shouldPass { format = formats.nixConf { package = pkgs.nix; @@ -1014,6 +1057,15 @@ runBuildTests { ''; }; + nixConfNull = shouldFail { + format = formats.nixConf { + package = pkgs.nix; + version = pkgs.nix.version; + extraOptions = "ignore-try = false"; + }; + input = null; + }; + phpAtoms = shouldPass rec { format = formats.php { finalVariable = "config"; }; input = { @@ -1043,6 +1095,16 @@ runBuildTests { ''; }; + phpNull = shouldPass { + format = formats.php { finalVariable = "config"; }; + input = null; + expected = '' + ''; }; + PlistNull = shouldPass { + format = formats.plist { }; + input = null; + expected = '' + + + + + ''; + }; + hcl1Atoms = shouldPass { format = formats.hcl1 { }; input = { From 3aebc40624d576bd95cc17da7db374e2582d6df0 Mon Sep 17 00:00:00 2001 From: K900 Date: Tue, 23 Jun 2026 20:59:38 +0300 Subject: [PATCH 27/31] nixos/test-driver: abort if we didn't connect to the backdoor in 5 minutes This is a lot of minutes, but less than the 1 hour it would normally take to time out the entire build. --- nixos/lib/test-driver/src/test_driver/machine/__init__.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nixos/lib/test-driver/src/test_driver/machine/__init__.py b/nixos/lib/test-driver/src/test_driver/machine/__init__.py index db96445af912..a202956abff4 100644 --- a/nixos/lib/test-driver/src/test_driver/machine/__init__.py +++ b/nixos/lib/test-driver/src/test_driver/machine/__init__.py @@ -1044,12 +1044,16 @@ class QemuMachine(BaseMachine): assert self.shell tic = time.time() - # TODO: do we want to bail after a set number of attempts? - while not shell_ready(timeout_secs=30): + + for _ in range(10): + if shell_ready(timeout_secs=30): + break self.log("Guest root shell did not produce any data yet...") self.log( " To debug, enter the VM and run 'systemctl status backdoor.service'." ) + else: + raise RuntimeError("Shell did not start in time") while True: chunk = self.shell.recv(1024) From efdeeebcb9127136cdb83f2212d785f0d33b1407 Mon Sep 17 00:00:00 2001 From: Yuriy Taraday Date: Tue, 23 Jun 2026 14:40:23 +0200 Subject: [PATCH 28/31] Reapply "pkgs-lib/formats: Use .attrs.json where possible" This expands on https://github.com/NixOS/nixpkgs/pull/498928 that introduced __structuredAttrs here by actually using data in `.attrs.json` when it makes sense, instead of relying on environment variables. This reverts commit bf6ada5d786e8aa4faff128e7e73a4792c2e99fe. This reapplies commit 691dc02df0111c320cd6697aacd4b113ba632e40. --- pkgs/pkgs-lib/formats.nix | 60 ++++++++++----------- pkgs/pkgs-lib/formats/configobj/default.nix | 8 +-- pkgs/pkgs-lib/formats/configobj/generate.py | 5 +- 3 files changed, 36 insertions(+), 37 deletions(-) diff --git a/pkgs/pkgs-lib/formats.nix b/pkgs/pkgs-lib/formats.nix index a184b0cbce53..f23f740ab7a9 100644 --- a/pkgs/pkgs-lib/formats.nix +++ b/pkgs/pkgs-lib/formats.nix @@ -146,14 +146,14 @@ optionalAttrs allowAliases aliases runCommand name { nativeBuildInputs = [ jq ]; - value = toJSON value; + inherit value; preferLocalBuild = true; __structuredAttrs = true; } + # NIX_ATTRS_JSON_FILE won't have `value` if it's null, but jq returns null for missing properties anyway + # jsonNull test keeps this in check '' - valuePath="$TMPDIR/value" - printf "%s" "$value" > "$valuePath" - jq . "$valuePath" > $out + jq .value "$NIX_ATTRS_JSON_FILE" > $out '' ) { }; @@ -171,14 +171,16 @@ optionalAttrs allowAliases aliases runCommand name { nativeBuildInputs = [ remarshal_0_17 ]; - value = toJSON value; + inherit value; preferLocalBuild = true; __structuredAttrs = true; } '' - valuePath="$TMPDIR/value" - printf "%s" "$value" > "$valuePath" - json2yaml "$valuePath" "$out" + json2yaml ${ + # attributes with null values are omitted from the JSON with structured attrs + # yaml_1_1Null test keeps this in check + if value == null then ''<(echo "null")'' else ''--unwrap value "$NIX_ATTRS_JSON_FILE"'' + } "$out" '' ) { }; @@ -196,14 +198,16 @@ optionalAttrs allowAliases aliases runCommand name { nativeBuildInputs = [ remarshal ]; - value = toJSON value; + inherit value; preferLocalBuild = true; __structuredAttrs = true; } '' - valuePath="$TMPDIR/value" - printf "%s" "$value" > "$valuePath" - json2yaml "$valuePath" "$out" + json2yaml ${ + # attributes with null values are omitted from the JSON with structured attrs + # yaml_1_2Null test keeps this in check + if value == null then ''<(echo "null")'' else ''--unwrap value "$NIX_ATTRS_JSON_FILE"'' + } "$out" '' ) { }; @@ -960,8 +964,10 @@ optionalAttrs allowAliases aliases python3 black ]; - imports = toJSON (value._imports or [ ]); - value = toJSON (removeAttrs value [ "_imports" ]); + imports = value._imports or [ ]; + # value must be an attrset, type would verify that, + # otherwise removeAttrs will fail. + value = removeAttrs value [ "_imports" ]; pythonGen = pkgs.writeText "pythonGen" '' import json import os @@ -984,26 +990,20 @@ optionalAttrs allowAliases aliases else: return repr(value) - with open(os.environ["importsPath"], "r") as f: - imports = json.load(f) - if imports is not None: - for i in imports: + with open(os.environ["NIX_ATTRS_JSON_FILE"], "r") as f: + attrs = json.load(f) + if attrs["imports"] is not None: + for i in attrs["imports"]: print(f"import {i}") print() - with open(os.environ["valuePath"], "r") as f: - for key, value in json.load(f).items(): + for key, value in attrs["value"].items(): print(f"{key} = {recursive_repr(value)}") ''; preferLocalBuild = true; __structuredAttrs = true; } '' - export importsPath="$TMPDIR/imports" - printf "%s" "$imports" > "$importsPath" - export valuePath="$TMPDIR/value" - printf "%s" "$value" > "$valuePath" - cat "$valuePath" python3 "$pythonGen" > $out black $out '' @@ -1039,14 +1039,16 @@ optionalAttrs allowAliases aliases python3Packages.xmltodict libxml2Python ]; - value = toJSON value; + inherit value; pythonGen = pkgs.writeText "pythonGen" '' import json import os import xmltodict - with open(os.environ["valuePath"], "r") as f: - print(xmltodict.unparse(json.load(f), full_document=${ + with open(os.environ["NIX_ATTRS_JSON_FILE"], "r") as f: + value = json.load(f).get("value") + assert type(value) is dict, "value must be an attrset" + print(xmltodict.unparse(value, full_document=${ if withHeader then "True" else "False" }, pretty=True, indent=" " * 2)) ''; @@ -1054,8 +1056,6 @@ optionalAttrs allowAliases aliases __structuredAttrs = true; } '' - export valuePath="$TMPDIR/value" - printf "%s" "$value" > "$valuePath" python3 "$pythonGen" > $out xmllint $out > /dev/null '' diff --git a/pkgs/pkgs-lib/formats/configobj/default.nix b/pkgs/pkgs-lib/formats/configobj/default.nix index 8179c4412e5f..227d8e7aa990 100644 --- a/pkgs/pkgs-lib/formats/configobj/default.nix +++ b/pkgs/pkgs-lib/formats/configobj/default.nix @@ -3,10 +3,6 @@ pkgs, }: let - inherit (lib) - toJSON - ; - inherit (lib.types) serializableValueWith ; @@ -25,12 +21,12 @@ in (pkgs.python3.withPackages (ps: [ ps.configobj ])) ]; - valuesJSON = toJSON value; + inherit value; __structuredAttrs = true; strictDeps = true; } '' - printf "%s" "$valuesJSON" | python3 ${./generate.py} > "$out" + python3 ${./generate.py} > "$out" ''; }; } diff --git a/pkgs/pkgs-lib/formats/configobj/generate.py b/pkgs/pkgs-lib/formats/configobj/generate.py index 5863396c120d..e531e68a4fbb 100644 --- a/pkgs/pkgs-lib/formats/configobj/generate.py +++ b/pkgs/pkgs-lib/formats/configobj/generate.py @@ -1,7 +1,10 @@ import json +import os import sys from configobj import ConfigObj config = ConfigObj(interpolation=False, encoding="UTF8") -config.update(json.load(sys.stdin)) +with open(os.environ["NIX_ATTRS_JSON_FILE"]) as f: + value = json.load(f).get("value") +config.update(value) config.write(sys.stdout.buffer) From b12656477c958885ea7469f18efe45967fee22c1 Mon Sep 17 00:00:00 2001 From: Atemu Date: Wed, 24 Jun 2026 00:43:16 +0200 Subject: [PATCH 29/31] Revert "nixos/system-path: don't link `/etc/xdg/` uncoditionally" This created more breakage than expected. Revert until the critical modules are patched. This reverts commit 603fc91c3326715ceb023220b193ff46046e7f08. --- doc/release-notes/rl-2611.section.md | 2 +- nixos/modules/config/system-path.nix | 1 + nixos/modules/config/xdg/autostart.nix | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/release-notes/rl-2611.section.md b/doc/release-notes/rl-2611.section.md index d9e27a469008..c3d9415e5459 100644 --- a/doc/release-notes/rl-2611.section.md +++ b/doc/release-notes/rl-2611.section.md @@ -10,7 +10,6 @@ -- Paths under `/etc/xdg/` from packages in `environment.systemPackages` are no longer linked into the global `/etc/` by default. Modules depending on such directories must declare them explicitly using `environment.pathsToLink`. - `databricks-cli` has been updated from `0.290.2` to `1.x.x`, the first major release. OAuth tokens for interactive logins (`auth_type = databricks-cli`) are now stored in the OS-native secure store by default (Secret Service on Linux) instead of `~/.databricks/token-cache.json`; cached tokens from older versions are not migrated, so run `databricks auth login` once per profile after upgrading. To keep the previous file-backed storage, set `DATABRICKS_AUTH_STORAGE=plaintext` or add `auth_storage = plaintext` under `[__settings__]` in `~/.databrickscfg`. Additionally, the `vector_search_endpoints` DABs resource renamed `min_qps` to `target_qps` (and the `vector-search-endpoints` command renamed `--min-qps` to `--target-qps`). See the [upstream changelog](https://github.com/databricks/cli/blob/main/CHANGELOG.md) for details. - `hurl` has been updated to `8.x.x` which has some breaking changes. See [upstream changelog](https://github.com/Orange-OpenSource/hurl/releases/tag/8.0.0) for details. @@ -94,3 +93,4 @@ ### Additions and Improvements {#sec-nixpkgs-release-26.11-lib-additions-improvements} - Create the first release note entry in this section! + diff --git a/nixos/modules/config/system-path.nix b/nixos/modules/config/system-path.nix index 5776c8ede413..c014f00da605 100644 --- a/nixos/modules/config/system-path.nix +++ b/nixos/modules/config/system-path.nix @@ -187,6 +187,7 @@ in environment.pathsToLink = [ "/bin" + "/etc/xdg" "/etc/gtk-2.0" "/etc/gtk-3.0" "/lib" # FIXME: remove and update debug-info.nix diff --git a/nixos/modules/config/xdg/autostart.nix b/nixos/modules/config/xdg/autostart.nix index a266b046de1a..46c90ae1793a 100644 --- a/nixos/modules/config/xdg/autostart.nix +++ b/nixos/modules/config/xdg/autostart.nix @@ -22,6 +22,8 @@ }; config = { + # FIXME this does not actually work because "/etc/xdg" is linked + # unconditionally in `nixos/modules/config/system-path.nix` environment.pathsToLink = lib.mkIf config.xdg.autostart.install [ "/etc/xdg/autostart" ]; From 0dc4ca4c71e3264818087b678c050fdbb3e3f87b Mon Sep 17 00:00:00 2001 From: Nina Fromm Date: Mon, 22 Jun 2026 17:35:25 +0200 Subject: [PATCH 30/31] nixos/tests: Load root profile in nspawn tests This is currently a hacky workarround, but bettern than nothing until we come up with a better solution. --- .../test-driver/src/test_driver/machine/__init__.py | 2 +- nixos/tests/simple-container.nix | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/nixos/lib/test-driver/src/test_driver/machine/__init__.py b/nixos/lib/test-driver/src/test_driver/machine/__init__.py index db96445af912..83e39c0be49b 100644 --- a/nixos/lib/test-driver/src/test_driver/machine/__init__.py +++ b/nixos/lib/test-driver/src/test_driver/machine/__init__.py @@ -1587,7 +1587,7 @@ class NspawnMachine(BaseMachine): # NOTE If the test calls switch-to-configuration (with a differently configured specialization) # this will use the /etc/profile of the new specialisation while `QemuMachine` nodes # will continue to use the original /etc/profile. - command = f"set -eo pipefail; source /etc/profile; set -u; {command}" + command = f"set -eo pipefail; USER=root HOME=/root source /etc/profile; set -u; {command}" cp = subprocess.run( [ diff --git a/nixos/tests/simple-container.nix b/nixos/tests/simple-container.nix index 269853423651..5cd3478f3d40 100644 --- a/nixos/tests/simple-container.nix +++ b/nixos/tests/simple-container.nix @@ -1,11 +1,16 @@ { name = "simple-container"; - containers.machine = { }; + containers = { + machine = { pkgs, ... }: { + users.users.root.packages = [ pkgs.hello ]; + }; + noprofile = { }; + }; testScript = '' start_all() - machine.wait_for_unit("multi-user.target") - machine.shutdown() + machine.succeed("hello") + noprofile.fail("hello") ''; } From 105327541bf13621cf79d3674543c17e2e5861a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20=C4=8Cun=C3=A1t?= Date: Wed, 24 Jun 2026 12:17:06 +0200 Subject: [PATCH 31/31] Reapply "staging-nixos merge for 2026-06-19" (#534864) This reverts commit 532f984da08d27af048ed8664238f97f38ede850, reversing changes made to 421ceaeef8fa1bb8b0729aec488d42b3af69a3fa. --- doc/release-notes/rl-2611.section.md | 1 + nixos/modules/config/system-path.nix | 1 - nixos/modules/config/xdg/autostart.nix | 2 - nixos/modules/system/boot/systemd/initrd.nix | 9 +++ nixos/modules/system/etc/etc-activation.nix | 8 +++ nixos/tests/all-tests.nix | 1 + nixos/tests/systemd-initrd-non-nixos.nix | 68 +++++++++++++++++++ pkgs/by-name/ni/nixos-init/src/find_etc.rs | 17 ++++- pkgs/by-name/ni/nixos-init/src/lib.rs | 51 ++++++++------ pkgs/by-name/st/strace/package.nix | 4 +- .../os-specific/linux/kernel/kernels-org.json | 4 +- 11 files changed, 135 insertions(+), 31 deletions(-) create mode 100644 nixos/tests/systemd-initrd-non-nixos.nix diff --git a/doc/release-notes/rl-2611.section.md b/doc/release-notes/rl-2611.section.md index 27bff446bb6e..d9e27a469008 100644 --- a/doc/release-notes/rl-2611.section.md +++ b/doc/release-notes/rl-2611.section.md @@ -10,6 +10,7 @@ +- Paths under `/etc/xdg/` from packages in `environment.systemPackages` are no longer linked into the global `/etc/` by default. Modules depending on such directories must declare them explicitly using `environment.pathsToLink`. - `databricks-cli` has been updated from `0.290.2` to `1.x.x`, the first major release. OAuth tokens for interactive logins (`auth_type = databricks-cli`) are now stored in the OS-native secure store by default (Secret Service on Linux) instead of `~/.databricks/token-cache.json`; cached tokens from older versions are not migrated, so run `databricks auth login` once per profile after upgrading. To keep the previous file-backed storage, set `DATABRICKS_AUTH_STORAGE=plaintext` or add `auth_storage = plaintext` under `[__settings__]` in `~/.databrickscfg`. Additionally, the `vector_search_endpoints` DABs resource renamed `min_qps` to `target_qps` (and the `vector-search-endpoints` command renamed `--min-qps` to `--target-qps`). See the [upstream changelog](https://github.com/databricks/cli/blob/main/CHANGELOG.md) for details. - `hurl` has been updated to `8.x.x` which has some breaking changes. See [upstream changelog](https://github.com/Orange-OpenSource/hurl/releases/tag/8.0.0) for details. diff --git a/nixos/modules/config/system-path.nix b/nixos/modules/config/system-path.nix index c014f00da605..5776c8ede413 100644 --- a/nixos/modules/config/system-path.nix +++ b/nixos/modules/config/system-path.nix @@ -187,7 +187,6 @@ in environment.pathsToLink = [ "/bin" - "/etc/xdg" "/etc/gtk-2.0" "/etc/gtk-3.0" "/lib" # FIXME: remove and update debug-info.nix diff --git a/nixos/modules/config/xdg/autostart.nix b/nixos/modules/config/xdg/autostart.nix index 46c90ae1793a..a266b046de1a 100644 --- a/nixos/modules/config/xdg/autostart.nix +++ b/nixos/modules/config/xdg/autostart.nix @@ -22,8 +22,6 @@ }; config = { - # FIXME this does not actually work because "/etc/xdg" is linked - # unconditionally in `nixos/modules/config/system-path.nix` environment.pathsToLink = lib.mkIf config.xdg.autostart.install [ "/etc/xdg/autostart" ]; diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index 6a52b920c2e2..311bf9cc9aa1 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -774,6 +774,7 @@ in ]; }; serviceConfig.Type = "oneshot"; + serviceConfig.EnvironmentFile = "-/etc/switch-root.conf"; description = "NixOS Activation"; script = # bash @@ -781,6 +782,14 @@ in set -uo pipefail export PATH="/bin:${cfg.package.util-linux}/bin" + # A non-NixOS closure (e.g. init=/bin/sh) has no prepare-root; + # initrd-find-nixos-closure records this as a non-empty NEW_INIT. + # Skip activation and let initrd-switch-root hand over to it directly. + if [ -n "''${NEW_INIT:-}" ]; then + echo "$NEW_INIT is not a NixOS system - not activating" + exit 0 + fi + closure="$(realpath /nixos-closure)" # Initialize the system diff --git a/nixos/modules/system/etc/etc-activation.nix b/nixos/modules/system/etc/etc-activation.nix index b00c25e37802..31842fc9c0c6 100644 --- a/nixos/modules/system/etc/etc-activation.nix +++ b/nixos/modules/system/etc/etc-activation.nix @@ -71,6 +71,10 @@ RequiresMountsFor = [ "/sysroot/nix/store" ]; + # find-etc only creates this symlink for a NixOS init. For a + # non-NixOS init= (e.g. init=/bin/sh) it is absent, so skip the + # mount instead of failing the whole initrd. + ConditionPathExists = "/etc-metadata-image"; }; requires = [ config.boot.initrd.systemd.services.initrd-find-etc.name @@ -123,6 +127,8 @@ "/run/nixos-etc-metadata" ]; DefaultDependencies = false; + # Skip for a non-NixOS init=, see the metadata mount above. + ConditionPathExists = "/etc-basedir"; }; } ]; @@ -140,6 +146,8 @@ # before the overlay is mounted. "/run/nixos-etc-metadata" ]; + # Skip for a non-NixOS init=, see the metadata mount above. + ConditionPathExists = "/etc-metadata-image"; }; serviceConfig = { Type = "oneshot"; diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 1c760d0648f0..d8971bd745e3 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1645,6 +1645,7 @@ in "i686-linux" ] ./initrd-network-openvpn { systemdStage1 = true; }; systemd-initrd-networkd-ssh = runTest ./systemd-initrd-networkd-ssh.nix; + systemd-initrd-non-nixos = runTest ./systemd-initrd-non-nixos.nix; systemd-initrd-shutdown = runTest { imports = [ ./systemd-shutdown.nix ]; _module.args.systemdStage1 = true; diff --git a/nixos/tests/systemd-initrd-non-nixos.nix b/nixos/tests/systemd-initrd-non-nixos.nix new file mode 100644 index 000000000000..0f564af6504a --- /dev/null +++ b/nixos/tests/systemd-initrd-non-nixos.nix @@ -0,0 +1,68 @@ +{ lib, pkgs, ... }: +let + marker = "REACHED NON-NIXOS INIT AS PID 1"; + + # A non-NixOS init (no prepare-root). We use a store path, not literal + # /bin/sh: a fresh disk has no /bin/sh yet (it is created by the activation a + # non-NixOS init skips), while the store is always mounted; init=/bin/sh works + # the same on a real system. Writes a marker, then stays alive so PID 1 lives. + nonNixosInit = pkgs.writeShellScriptBin "non-nixos" '' + echo "${marker}" > /dev/console + exec ${pkgs.coreutils}/bin/sleep infinity + ''; + + common = { + boot.initrd.systemd.enable = true; + + virtualisation = { + # tmpfs root, like real non-NixOS closure init= microvm consumers. + diskImage = null; + + graphics = false; + }; + + # Speed up wait_for_console. + boot.consoleLogLevel = lib.mkForce 3; + boot.initrd.systemd.managerEnvironment.SYSTEMD_LOG_LEVEL = "warning"; + + # switch-root needs an os-release on the target root. A real system has one + # on disk; our fresh tmpfs does not, so create it. + boot.initrd.systemd.tmpfiles.settings."10-os-release"."/sysroot/etc/os-release".f = { + mode = "0644"; + argument = "ID=test-non-nixos"; + }; + }; +in +{ + name = "systemd-initrd-non-nixos"; + + nodes = { + bashActivation = common; + + nixosInit = { + imports = [ common ]; + system.nixos-init.enable = true; + system.etc.overlay.enable = true; + services.userborn.enable = true; + }; + }; + + testScript = '' + import os + + # The last init= on the cmdline wins; QEMU_KERNEL_PARAMS is appended after + # the default one, so this boots our non-NixOS init. + os.environ["QEMU_KERNEL_PARAMS"] = "init=${lib.getExe nonNixosInit}" + + start_all() + + # If a code path does not skip the non-NixOS init, switch-root is blocked and + # the machine drops to emergency mode: the marker never appears and the wait + # times out. + with subtest("bash initrd-nixos-activation skips a non-NixOS init"): + bashActivation.wait_for_console_text("${marker}", timeout=300) + + with subtest("nixos-init switches to a non-NixOS init directly"): + nixosInit.wait_for_console_text("${marker}", timeout=300) + ''; +} diff --git a/pkgs/by-name/ni/nixos-init/src/find_etc.rs b/pkgs/by-name/ni/nixos-init/src/find_etc.rs index 8b994e1d7534..d06f7bbb0114 100644 --- a/pkgs/by-name/ni/nixos-init/src/find_etc.rs +++ b/pkgs/by-name/ni/nixos-init/src/find_etc.rs @@ -3,7 +3,7 @@ use std::{os::unix, path::Path}; use anyhow::{Context, Result}; use crate::config::Config; -use crate::{SYSROOT_PATH, find_toplevel_in_prefix, resolve_in_prefix}; +use crate::{SYSROOT_PATH, find_init_in_prefix, resolve_in_prefix, verify_init_is_nixos}; /// Entrypoint for the `find-etc` binary. /// @@ -12,7 +12,20 @@ use crate::{SYSROOT_PATH, find_toplevel_in_prefix, resolve_in_prefix}; /// This avoids needing a reference to the toplevel embedded in the initrd and thus reduces the /// need to re-build it. pub fn find_etc() -> Result<()> { - let toplevel = find_toplevel_in_prefix(SYSROOT_PATH)?; + let init_in_sysroot = + find_init_in_prefix(SYSROOT_PATH).context("Failed to find init in sysroot")?; + + // A non-NixOS init= (e.g. init=/bin/sh) has no etc metadata image. Skip + // without creating the symlinks: the etc-overlay mounts are gated on them + // and so skip too, and initrd-init switches root to the init directly. + let Ok(toplevel) = verify_init_is_nixos(SYSROOT_PATH, &init_in_sysroot) else { + log::info!( + "{} is not a NixOS system - not setting up the etc overlay.", + init_in_sysroot.display() + ); + return Ok(()); + }; + let config = Config::from_toplevel(&toplevel, SYSROOT_PATH)?; let basedir = config diff --git a/pkgs/by-name/ni/nixos-init/src/lib.rs b/pkgs/by-name/ni/nixos-init/src/lib.rs index c781757c9b82..bbb4abdb829b 100644 --- a/pkgs/by-name/ni/nixos-init/src/lib.rs +++ b/pkgs/by-name/ni/nixos-init/src/lib.rs @@ -27,16 +27,6 @@ pub use crate::{ pub const SYSROOT_PATH: &str = "/sysroot"; -/// Find the path to the toplevel closure of the system in a prefix. -/// -/// Uses the `init=` parameter on the kernel command-line. -/// -/// Returns the relative path of the init to the prefix, e.g. without the `/sysroot` prefix. -pub fn find_toplevel_in_prefix(prefix: &str) -> Result { - let init_in_sysroot = find_init_in_prefix(prefix)?; - verify_init_is_nixos(prefix, init_in_sysroot) -} - /// Verify that an init path is inside a `NixOS` toplevel directory. /// /// If the path is verified, returns the path to the toplevel. @@ -87,20 +77,16 @@ pub fn find_init_in_prefix(prefix: &str) -> Result { } /// Extract the value of the `init` parameter from the given kernel `cmdline`. +/// +/// If `init=` appears multiple times the last one wins, matching the kernel. +/// This is what makes appending `init=/bin/sh` at the boot prompt work even +/// though the boot entry already has an `init=`. fn extract_init(cmdline: &str) -> Result { - let init_params: Vec<&str> = cmdline + let init = cmdline .split_ascii_whitespace() - .filter(|p| p.starts_with("init=")) - .collect(); - - if init_params.len() != 1 { - bail!("Expected exactly one init param on kernel cmdline: {cmdline}") - } - - let init = init_params - .first() - .and_then(|s| s.split('=').next_back()) - .context("Failed to extract init path from kernel cmdline: {cmdline}")?; + .filter_map(|p| p.strip_prefix("init=")) + .next_back() + .with_context(|| format!("No init= parameter on kernel cmdline: {cmdline}"))?; Ok(PathBuf::from(init)) } @@ -129,4 +115,25 @@ mod tests { Ok(()) } + + #[test] + fn test_extract_init_single() { + assert_eq!( + extract_init("root=fstab init=/nix/store/xxx-nixos/init quiet").unwrap(), + PathBuf::from("/nix/store/xxx-nixos/init") + ); + } + + #[test] + fn test_extract_init_last_wins() { + assert_eq!( + extract_init("init=/nix/store/xxx-nixos/init init=/bin/sh").unwrap(), + PathBuf::from("/bin/sh") + ); + } + + #[test] + fn test_extract_init_missing() { + assert!(extract_init("root=fstab quiet").is_err()); + } } diff --git a/pkgs/by-name/st/strace/package.nix b/pkgs/by-name/st/strace/package.nix index 86f8b0fa4c42..77345bd6a3ae 100644 --- a/pkgs/by-name/st/strace/package.nix +++ b/pkgs/by-name/st/strace/package.nix @@ -11,11 +11,11 @@ stdenv.mkDerivation (finalAttrs: { pname = "strace"; - version = "7.0"; + version = "7.1"; src = fetchurl { url = "https://strace.io/files/${finalAttrs.version}/strace-${finalAttrs.version}.tar.xz"; - hash = "sha256-bJJBm+Py7FYLMXKKRlIhfFmGTIZCunsbN3GxsBOtB0s="; + hash = "sha256-gXQ+zypbRBhrL1A4r9yL7aflxwrtFbT7+8xuns4kSQ8="; }; separateDebugInfo = true; diff --git a/pkgs/os-specific/linux/kernel/kernels-org.json b/pkgs/os-specific/linux/kernel/kernels-org.json index d3dd8012c508..54ff5434cfe3 100644 --- a/pkgs/os-specific/linux/kernel/kernels-org.json +++ b/pkgs/os-specific/linux/kernel/kernels-org.json @@ -30,8 +30,8 @@ "lts": true }, "6.18": { - "version": "6.18.35", - "hash": "sha256:0dpjprjzc4w44kw49jcgx1ffrm6gxn2gsnsz3hhmw4hr4a9h51pp", + "version": "6.18.36", + "hash": "sha256:0kn4r43lnd5nb5c298b30030qyaxv05s7k40n9si1j3iyk4qdazv", "lts": true }, "7.0": {