diff --git a/pkgs/by-name/ni/nixos-init/src/lib.rs b/pkgs/by-name/ni/nixos-init/src/lib.rs index c781757c9b82..3d38065f9a88 100644 --- a/pkgs/by-name/ni/nixos-init/src/lib.rs +++ b/pkgs/by-name/ni/nixos-init/src/lib.rs @@ -87,20 +87,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 +125,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()); + } }