nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1use std::{
2 fs,
3 path::{Path, PathBuf},
4};
5
6use anyhow::{Context, Result};
7
8use crate::{config::Config, fs::atomic_symlink};
9
10/// Activate the system.
11///
12/// This runs both during boot and during re-activation initiated by switch-to-configuration.
13pub fn activate(prefix: &str, toplevel: impl AsRef<Path>, config: &Config) -> Result<()> {
14 log::info!("Setting up /run/current-system...");
15 let system_path = PathBuf::from(prefix).join("run/current-system");
16 atomic_symlink(&toplevel, system_path)?;
17
18 log::info!("Setting up modprobe...");
19 setup_modprobe(&config.modprobe_binary)?;
20
21 log::info!("Setting up firmware search paths...");
22 setup_firmware_search_path(&config.firmware)?;
23
24 if let Some(env_path) = &config.env_binary {
25 log::info!("Setting up /usr/bin/env...");
26 setup_usrbinenv(prefix, env_path)?;
27 } else {
28 log::info!("No env binary provided. Not setting up /usr/bin/env.");
29 }
30
31 if let Some(sh_path) = &config.sh_binary {
32 log::info!("Setting up /bin/sh...");
33 setup_binsh(prefix, sh_path)?;
34 } else {
35 log::info!("No sh binary provided. Not setting up /bin/sh.");
36 }
37
38 Ok(())
39}
40
41/// Setup modprobe so that the kernel can find the wrapped binary.
42///
43/// See <https://docs.kernel.org/admin-guide/sysctl/kernel.html#modprobe>
44fn setup_modprobe(modprobe_binary: impl AsRef<Path>) -> Result<()> {
45 // This uses the procfs setup in the initrd, which is fine because it points to the same kernel
46 // a procfs in a chroot would.
47 const MODPROBE_PATH: &str = "/proc/sys/kernel/modprobe";
48
49 if Path::new(MODPROBE_PATH).exists() {
50 fs::write(
51 MODPROBE_PATH,
52 modprobe_binary.as_ref().as_os_str().as_encoded_bytes(),
53 )
54 .with_context(|| {
55 format!(
56 "Failed to populate modprobe path with {}",
57 modprobe_binary.as_ref().display()
58 )
59 })?;
60 } else {
61 log::info!("{MODPROBE_PATH} doesn't exist. Not populating it...");
62 }
63
64 Ok(())
65}
66
67/// Setup the firmware search path so that the kernel can find the firmware.
68///
69/// See <https://www.kernel.org/doc/html/latest/driver-api/firmware/fw_search_path.html>
70fn setup_firmware_search_path(firmware: impl AsRef<Path>) -> Result<()> {
71 // This uses the sysfs setup in the initrd, which is fine because it points to the same kernel
72 // a procfs in a chroot would.
73 const FIRMWARE_SERCH_PATH: &str = "/sys/module/firmware_class/parameters/path";
74
75 if Path::new(FIRMWARE_SERCH_PATH).exists() {
76 fs::write(
77 FIRMWARE_SERCH_PATH,
78 firmware.as_ref().as_os_str().as_encoded_bytes(),
79 )
80 .with_context(|| {
81 format!(
82 "Failed to populate firmware search path with {}",
83 firmware.as_ref().display()
84 )
85 })?;
86 } else {
87 log::info!("{FIRMWARE_SERCH_PATH} doesn't exist. Not populating it...");
88 }
89
90 Ok(())
91}
92
93/// Setup `/usr/bin/env`.
94///
95/// We have to setup `/usr` for `NixOS` to work.
96///
97/// We do this here accidentally. `/usr/bin/env` is currently load-bearing for `NixOS`.
98fn setup_usrbinenv(prefix: &str, env_binary: impl AsRef<Path>) -> Result<()> {
99 let usrbin_path = PathBuf::from(prefix).join("usr/bin");
100 fs::create_dir_all(&usrbin_path).context("Failed to create /usr/bin")?;
101 atomic_symlink(&env_binary, usrbin_path.join("env"))
102}
103
104/// Setup /bin/sh.
105///
106/// `/bin/sh` is an essential part of a Linux system as this path is hardcoded in the `system()` call
107/// from libc. See `man systemd(3)`.
108fn setup_binsh(prefix: &str, sh_binary: impl AsRef<Path>) -> Result<()> {
109 let binsh_path = PathBuf::from(prefix).join("bin/sh");
110 atomic_symlink(&sh_binary, binsh_path)
111}