nixos-init: read the required paths from a file in the toplevel instead of from the environment
This is a first step towards using nixos-init also during re-activation.
This commit is contained in:
@@ -20,28 +20,49 @@ in
|
||||
package = lib.mkPackageOption pkgs "nixos-init" { };
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = config.boot.initrd.systemd.enable;
|
||||
message = "nixos-init can only be used with boot.initrd.systemd.enable";
|
||||
}
|
||||
{
|
||||
assertion = config.system.etc.overlay.enable;
|
||||
message = "nixos-init can only be used with system.etc.overlay.enable";
|
||||
}
|
||||
{
|
||||
assertion = config.services.userborn.enable || config.systemd.sysusers.enable;
|
||||
message = "nixos-init can only be used with services.userborn.enable or systemd.sysusers.enable";
|
||||
}
|
||||
{
|
||||
assertion = config.boot.postBootCommands == "";
|
||||
message = "nixos-init cannot be used with boot.postBootCommands";
|
||||
}
|
||||
{
|
||||
assertion = config.powerManagement.powerUpCommands == "";
|
||||
message = "nixos-init cannot be used with powerManagement.powerUpCommands";
|
||||
}
|
||||
];
|
||||
};
|
||||
config = lib.mkMerge [
|
||||
{
|
||||
boot.bootspec.extensions = {
|
||||
"org.nixos.nixos-init.v1" = {
|
||||
firmware = "${config.hardware.firmware}/lib/firmware";
|
||||
modprobe_binary = "${pkgs.kmod}/bin/modprobe";
|
||||
nix_store_mount_opts = config.boot.nixStoreMountOpts;
|
||||
}
|
||||
// lib.optionalAttrs (config.environment.binsh != null) {
|
||||
sh_binary = config.environment.binsh;
|
||||
}
|
||||
// lib.optionalAttrs (config.environment.usrbinenv != null) {
|
||||
env_binary = config.environment.usrbinenv;
|
||||
}
|
||||
// lib.optionalAttrs config.system.etc.overlay.enable {
|
||||
etc_metadata_image = config.system.build.etcMetadataImage;
|
||||
etc_basedir = config.system.build.etcBasedir;
|
||||
};
|
||||
};
|
||||
}
|
||||
(lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = config.boot.initrd.systemd.enable;
|
||||
message = "nixos-init can only be used with boot.initrd.systemd.enable";
|
||||
}
|
||||
{
|
||||
assertion = config.system.etc.overlay.enable;
|
||||
message = "nixos-init can only be used with system.etc.overlay.enable";
|
||||
}
|
||||
{
|
||||
assertion = config.services.userborn.enable || config.systemd.sysusers.enable;
|
||||
message = "nixos-init can only be used with services.userborn.enable or systemd.sysusers.enable";
|
||||
}
|
||||
{
|
||||
assertion = config.boot.postBootCommands == "";
|
||||
message = "nixos-init cannot be used with boot.postBootCommands";
|
||||
}
|
||||
{
|
||||
assertion = config.powerManagement.powerUpCommands == "";
|
||||
message = "nixos-init cannot be used with powerManagement.powerUpCommands";
|
||||
}
|
||||
];
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
@@ -33,11 +33,6 @@ let
|
||||
|
||||
ln -s ${config.system.build.etc}/etc $out/etc
|
||||
|
||||
${lib.optionalString config.system.etc.overlay.enable ''
|
||||
ln -s ${config.system.build.etcMetadataImage} $out/etc-metadata-image
|
||||
ln -s ${config.system.build.etcBasedir} $out/etc-basedir
|
||||
''}
|
||||
|
||||
ln -s ${config.system.path} $out/sw
|
||||
ln -s "$systemd" $out/systemd
|
||||
|
||||
|
||||
@@ -733,17 +733,6 @@ in
|
||||
cfg.package.util-linux
|
||||
config.system.nixos-init.package
|
||||
];
|
||||
environment = {
|
||||
FIRMWARE = "${config.hardware.firmware}/lib/firmware";
|
||||
MODPROBE_BINARY = "${pkgs.kmod}/bin/modprobe";
|
||||
NIX_STORE_MOUNT_OPTS = lib.concatStringsSep "," config.boot.nixStoreMountOpts;
|
||||
}
|
||||
// lib.optionalAttrs (config.environment.usrbinenv != null) {
|
||||
ENV_BINARY = config.environment.usrbinenv;
|
||||
}
|
||||
// lib.optionalAttrs (config.environment.binsh != null) {
|
||||
SH_BINARY = config.environment.binsh;
|
||||
};
|
||||
serviceConfig = {
|
||||
ExecStart = [
|
||||
""
|
||||
|
||||
Generated
+130
@@ -14,6 +14,17 @@ version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
|
||||
[[package]]
|
||||
name = "bootspec"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75726e2aa2b4c5a9d5c4cf3cb7f24658b6ec861616088f3ef3fb72edc0599286"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.1"
|
||||
@@ -73,6 +84,12 @@ version = "2.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
@@ -91,14 +108,23 @@ version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "nixos-init"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bootspec",
|
||||
"env_logger",
|
||||
"indoc",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
@@ -108,6 +134,24 @@ version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.3.0"
|
||||
@@ -127,6 +171,66 @@ dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.20.0"
|
||||
@@ -140,6 +244,32 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.2+wasi-0.2.4"
|
||||
|
||||
@@ -7,6 +7,9 @@ edition = "2024"
|
||||
anyhow = "1.0.98"
|
||||
log = "0.4.27"
|
||||
env_logger = { version = "0.11.8", default-features = false }
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_json = "1.0.145"
|
||||
bootspec = "2.0.0"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.20.0"
|
||||
|
||||
@@ -1,43 +1,39 @@
|
||||
use std::env;
|
||||
use serde::Deserialize;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use bootspec::BootJson;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Config {
|
||||
pub firmware: String,
|
||||
pub modprobe_binary: String,
|
||||
pub nix_store_mount_opts: Vec<String>,
|
||||
pub env_binary: Option<String>,
|
||||
pub sh_binary: Option<String>,
|
||||
pub etc_basedir: Option<String>,
|
||||
pub etc_metadata_image: Option<String>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Read the config from the environment.
|
||||
///
|
||||
/// These options are provided by wrapping the binary when assembling the toplevel.
|
||||
pub fn from_env() -> Result<Self> {
|
||||
let nix_store_mount_opts = required("NIX_STORE_MOUNT_OPTS")?
|
||||
.split(',')
|
||||
.map(std::borrow::ToOwned::to_owned)
|
||||
.collect();
|
||||
/// Read the config from the metadata file in the toplevel directory.
|
||||
pub fn from_toplevel(toplevel: impl AsRef<Path>, prefix: &str) -> Result<Self> {
|
||||
let bootspec_path =
|
||||
PathBuf::from(prefix).join(toplevel.as_ref().join("boot.json").strip_prefix("/")?);
|
||||
|
||||
Ok(Self {
|
||||
firmware: required("FIRMWARE")?,
|
||||
modprobe_binary: required("MODPROBE_BINARY")?,
|
||||
nix_store_mount_opts,
|
||||
env_binary: optional("ENV_BINARY"),
|
||||
sh_binary: optional("SH_BINARY"),
|
||||
})
|
||||
let boot_json: BootJson = fs::read(bootspec_path)
|
||||
.context("Failed to read bootspec file")
|
||||
.and_then(|raw| serde_json::from_slice(&raw).context("Failed to read bootspec JSON"))?;
|
||||
|
||||
let config = boot_json
|
||||
.extensions
|
||||
.get("org.nixos.nixos-init.v1")
|
||||
.context("Failed to extract nixos-init bootspec extension")
|
||||
.and_then(|v| {
|
||||
serde_json::from_value(v.clone()).context("Failed to deserialise config")
|
||||
})?;
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a required environment variable
|
||||
///
|
||||
/// Fail with useful context if the variable is not set in the environment.
|
||||
fn required(key: &str) -> Result<String> {
|
||||
env::var(key).with_context(|| format!("Failed to read {key} from environment"))
|
||||
}
|
||||
|
||||
/// Read an optional environment variable
|
||||
fn optional(key: &str) -> Option<String> {
|
||||
env::var(key).ok()
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::{os::unix, path::Path};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::{SYSROOT_PATH, canonicalize_in_chroot, find_toplevel_in_prefix};
|
||||
|
||||
/// Entrypoint for the `find-etc` binary.
|
||||
@@ -12,20 +13,23 @@ use crate::{SYSROOT_PATH, canonicalize_in_chroot, find_toplevel_in_prefix};
|
||||
/// need to re-build it.
|
||||
pub fn find_etc() -> Result<()> {
|
||||
let toplevel = find_toplevel_in_prefix(SYSROOT_PATH)?;
|
||||
let config = Config::from_toplevel(&toplevel, SYSROOT_PATH)?;
|
||||
|
||||
let etc_metadata_image = Path::new(SYSROOT_PATH).join(
|
||||
canonicalize_in_chroot(SYSROOT_PATH, &toplevel.join("etc-metadata-image"))?
|
||||
.strip_prefix("/")?,
|
||||
);
|
||||
let basedir = config
|
||||
.etc_basedir
|
||||
.context("Failed to read etc_basedir from bootspec")?;
|
||||
let etc_basedir = Path::new(SYSROOT_PATH)
|
||||
.join(canonicalize_in_chroot(SYSROOT_PATH, Path::new(&basedir))?.strip_prefix("/")?);
|
||||
|
||||
let etc_basedir = Path::new(SYSROOT_PATH).join(
|
||||
canonicalize_in_chroot(SYSROOT_PATH, &toplevel.join("etc-basedir"))?.strip_prefix("/")?,
|
||||
);
|
||||
let metadata_image = config
|
||||
.etc_metadata_image
|
||||
.context("Failed to read etc_metadata_image from bootspec")?;
|
||||
let etc_metadata_image = Path::new(SYSROOT_PATH)
|
||||
.join(canonicalize_in_chroot(SYSROOT_PATH, Path::new(&metadata_image))?.strip_prefix("/")?);
|
||||
|
||||
unix::fs::symlink(etc_basedir, "/etc-basedir").context("Failed to link /etc-basedir")?;
|
||||
unix::fs::symlink(etc_metadata_image, "/etc-metadata-image")
|
||||
.context("Failed to link /etc-metadata-image")?;
|
||||
|
||||
unix::fs::symlink(etc_basedir, "/etc-basedir").context("Failed to link /etc-basedir")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ fn remount_nix_store(prefix: &str, nix_store_mount_opts: &[String]) -> Result<()
|
||||
if let Some(last_nix_store_mount) = mounts.find_mountpoint(&nix_store_path) {
|
||||
for opt in nix_store_mount_opts {
|
||||
if !last_nix_store_mount.mntopts.contains(opt) {
|
||||
missing_opts.push(opt.to_string());
|
||||
missing_opts.push(opt.clone());
|
||||
}
|
||||
}
|
||||
if !missing_opts.is_empty() {
|
||||
|
||||
@@ -8,13 +8,14 @@ use crate::{
|
||||
///
|
||||
/// Initialize `NixOS` from a systemd initrd.
|
||||
pub fn initrd_init() -> Result<()> {
|
||||
let config = Config::from_env().context("Failed to get configuration")?;
|
||||
let init_in_sysroot =
|
||||
find_init_in_prefix(SYSROOT_PATH).context("Failed to find init in sysroot")?;
|
||||
|
||||
let init_path = if let Ok(toplevel) = verify_init_is_nixos(SYSROOT_PATH, &init_in_sysroot) {
|
||||
log::info!("Initializing NixOS...");
|
||||
init(SYSROOT_PATH, toplevel, &config)?;
|
||||
let config = Config::from_toplevel(&toplevel, SYSROOT_PATH)
|
||||
.context("Failed to get configuration")?;
|
||||
init(SYSROOT_PATH, &toplevel, &config)?;
|
||||
None
|
||||
} else {
|
||||
log::info!("Not initializing NixOS. Switching to new root immediately...");
|
||||
|
||||
Reference in New Issue
Block a user