/// Prior to invoking `systemctl switch-root`, some setup work is required.
/// Mainly, we need to fiddle with mounts so that /sysroot is the rw snapshot for
/// the current bootid. This is necessary so that the newly invoked systemd has
/// the correct root mount point.
Prior to invoking `systemctl switch-root`, some setup work is required.
Mainly, we need to fiddle with mounts so that /sysroot is the rw snapshot for
the current bootid. This is necessary so that the newly invoked systemd has
the correct root mount point.
Prior to invoking ` systemctl switch - root ` some setup work is required . Mainly we need to fiddle with mounts so that / sysroot is the rw snapshot for the current bootid . This is necessary so that the newly invoked systemd has the correct root mount point .
pub async fn switch_root(log: Logger, opts: Opts) -> Result<()> {
    let (device, options) = find_rootdisk_device().context("failed to find /rootdisk device")?;
    let mut options: Vec<_> = options.split(',').map(|s| s.to_string()).collect();
    let mut log = log.new(o!());
    if let Some(snapshot) = opts.snapshot {
        log = log.new(o!("snapshot" => snapshot.clone()));
        options = replace_subvol(options, &snapshot)
            .context("failed to replace subvolume in mount options")?;
    }
    std::fs::create_dir("/sysroot").context("failed to mkdir /sysroot")?;
    debug!(
        log,
        "mounting subvolume on {} with options {:?}", device, options
    );
    mount(
        log.clone(),
        MountOpts {
            bind: false,
            source: device.clone(),
            target: "/sysroot".into(),
            fstype: Some("btrfs".into()),
            options: options.clone(),
        },
    )
    .with_context(|| format!("failed to mount '{}' on /sysroot {:?}", device, options))?;
    // If we have a host config, mount the kernel modules pre-switch-root
    // instead of waiting for a unit in the rootfs. This way, modules can be
    // used by a systemd generator if necessary
    if let Ok(host_config_uri) = std::env::var("HOST_CONFIG_URI") {
        let uri = host_config_uri
            .parse()
            .with_context(|| format!("'{}' is not a valid uri", host_config_uri))?;
        let host_config = get_host_config::get_host_config(&uri)
            .await
            .with_context(|| format!("while getting host config from {}", uri))?;
        let kernel_subvol = host_config
            .boot_config
            .kernel
            .pkg
            .on_disk()
            .context("kernel subvol not on disk")?;
        let utsname = nix::sys::utsname::uname();
        let mountpoint = Path::new("/sysroot/usr/lib/modules").join(utsname.release());
        std::fs::create_dir_all(&mountpoint).with_context(|| {
            format!(
                "while creating kernel modules mountpoint {}",
                mountpoint.display()
            )
        })?;
        let modules_dir = kernel_subvol
            .path()
            .join("modules")
            .to_str()
            .map(str::to_string);
        mount(
            log.clone(),
            MountOpts {
                bind: true,
                source: modules_dir.context("modules dir not a string")?,
                target: mountpoint.clone(),
                fstype: Some("btrfs".into()),
                options: vec![],
            },
        )
        .with_context(|| format!("while mounting kernel modules at {}", mountpoint.display()))?;
    }
    let sd = Systemd::connect(log.clone()).await?;
    // systemctl daemon-reload is necessary after mounting the
    // to-switch-root-into snapshot at /sysroot, since systemd will
    // automatically reload some unit configuration from /sysroot when running
    // inside the initrd, and this behavior is necessary to pass the correct
    // state of units into the new systemd in the root fs.
    debug!(log, "requesting systemd reload");
    sd.reload()
        .await
        .context("failed to reload systemd units (systemctl daemon-reload)")?;
    debug!(log, "switch-rooting into /sysroot");
    // ask systemd to switch-root to the new root fs
    sd.switch_root(FilePath::new("/sysroot"), FilePath::new(""))
        .await
        .context("failed to trigger switch-root (systemctl switch-root /syroot)")
}
pub async fn switch_root ( log : Logger , opts : Opts ) -> Result < ( ) > { let ( device , options ) = find_rootdisk_device ( ) . context ( " " ) ? ; let mut options : Vec < _ > = options . split ( ',' ) . map ( | s | s . to_string ( ) ) . collect ( ) ; let mut log = log . new ( o ! ( ) ) ; if let Some ( snapshot ) = opts . snapshot { log = log . new ( o ! ( " " snapshot clone ( ) ) ) ; options = replace_subvol ( options , & snapshot ) . context ( " " ) ? ; } std :: fs :: create_dir ( " " ) . context ( " " ) ? ; debug ! ( log " " device options ) ; mount ( log . clone ( ) , MountOpts { bind : false , source : device . clone ( ) , target : " " . into ( ) , fstype : Some ( " " . into ( ) ) , options : options . clone ( ) , } , ) . with_context ( | | format ! ( " " device options ) ) ? ; // If we have a host config, mount the kernel modules pre-switch-root // instead of waiting for a unit in the rootfs. This way, modules can be // used by a systemd generator if necessary if let Ok ( host_config_uri ) = std :: env :: var ( " " ) { let uri = host_config_uri . parse ( ) . with_context ( | | format ! ( " " host_config_uri ) ) ? ; let host_config = get_host_config :: get_host_config ( & uri ) . await . with_context ( | | format ! ( " " uri ) ) ? ; let kernel_subvol = host_config . boot_config . kernel . pkg . on_disk ( ) . context ( " " ) ? ; let utsname = nix :: sys :: utsname :: uname ( ) ; let mountpoint = Path :: new ( " " ) . join ( utsname . release ( ) ) ; std :: fs :: create_dir_all ( & mountpoint ) . with_context ( | | { format ! ( " " mountpoint display ( ) ) } ) ? ; let modules_dir = kernel_subvol . path ( ) . join ( " " ) . to_str ( ) . map ( str :: to_string ) ; mount ( log . clone ( ) , MountOpts { bind : true , source : modules_dir . context ( " " ) ? , target : mountpoint . clone ( ) , fstype : Some ( " " . into ( ) ) , options : vec ! [ ] , } , ) . with_context ( | | format ! ( " " mountpoint display ( ) ) ) ? ; } let sd = Systemd :: connect ( log . clone ( ) ) . await ? ; // systemctl daemon-reload is necessary after mounting the // to-switch-root-into snapshot at /sysroot, since systemd will // automatically reload some unit configuration from /sysroot when running // inside the initrd, and this behavior is necessary to pass the correct // state of units into the new systemd in the root fs. debug ! ( log " " ) ; sd . reload ( ) . await . context ( " " ) ? ; debug ! ( log " " ) ; // ask systemd to switch-root to the new root fs sd . switch_root ( FilePath :: new ( " " ) , FilePath :: new ( " " ) ) . await . context ( " " ) }
Prior to invoking `systemctl switch-root`, some setup work is required.
Prior to invoking ` systemctl switch - root ` some setup work is required .
log
Logger
opts
Opts