diff --git a/lib/src/cli.rs b/lib/src/cli.rs index 7d0fbea62..42a9925ed 100644 --- a/lib/src/cli.rs +++ b/lib/src/cli.rs @@ -357,6 +357,7 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> { } } else { let fetched = crate::deploy::pull(sysroot, imgref, opts.quiet).await?; + let mut kargs = crate::deploy::get_kargs(repo, fetched.as_ref())?; let staged_digest = staged_image.as_ref().map(|s| s.image_digest.as_str()); let fetched_digest = fetched.manifest_digest.as_str(); tracing::debug!("staged: {staged_digest:?}"); @@ -378,7 +379,10 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> { println!("No update available.") } else { let osname = booted_deployment.osname(); - crate::deploy::stage(sysroot, &osname, &fetched, &spec).await?; + let mut opts = ostree::SysrootDeployTreeOpts::default(); + let kargs: Vec<&str> = kargs.iter_mut().map(|s| s.as_str() ).collect(); + opts.override_kernel_argv = Some(kargs.as_slice()); + crate::deploy::stage(sysroot, &osname, &fetched, &spec, Some(opts)).await?; changed = true; if let Some(prev) = booted_image.as_ref() { if let Some(fetched_manifest) = fetched.get_manifest(repo)? { @@ -451,6 +455,7 @@ async fn switch(opts: SwitchOpts) -> Result<()> { let new_spec = RequiredHostSpec::from_spec(&new_spec)?; let fetched = crate::deploy::pull(sysroot, &target, opts.quiet).await?; + let mut kargs = crate::deploy::get_kargs(repo, fetched.as_ref())?; if !opts.retain { // By default, we prune the previous ostree ref so it will go away after later upgrades @@ -464,7 +469,10 @@ async fn switch(opts: SwitchOpts) -> Result<()> { } let stateroot = booted_deployment.osname(); - crate::deploy::stage(sysroot, &stateroot, &fetched, &new_spec).await?; + let mut opts = ostree::SysrootDeployTreeOpts::default(); + let kargs: Vec<&str> = kargs.iter_mut().map(|s| s.as_str() ).collect(); + opts.override_kernel_argv = Some(kargs.as_slice()); + crate::deploy::stage(sysroot, &stateroot, &fetched, &new_spec, Some(opts)).await?; Ok(()) } @@ -493,11 +501,16 @@ async fn edit(opts: EditOpts) -> Result<()> { } let new_spec = RequiredHostSpec::from_spec(&new_host.spec)?; let fetched = crate::deploy::pull(sysroot, new_spec.image, opts.quiet).await?; + let repo = &sysroot.repo(); + let mut kargs = crate::deploy::get_kargs(repo, fetched.as_ref())?; // TODO gc old layers here let stateroot = booted_deployment.osname(); - crate::deploy::stage(sysroot, &stateroot, &fetched, &new_spec).await?; + let mut opts = ostree::SysrootDeployTreeOpts::default(); + let kargs: Vec<&str> = kargs.iter_mut().map(|s| s.as_str() ).collect(); + opts.override_kernel_argv = Some(kargs.as_slice()); + crate::deploy::stage(sysroot, &stateroot, &fetched, &new_spec, Some(opts)).await?; Ok(()) } diff --git a/lib/src/deploy.rs b/lib/src/deploy.rs index 90c5e40aa..3f98d556a 100644 --- a/lib/src/deploy.rs +++ b/lib/src/deploy.rs @@ -16,6 +16,9 @@ use ostree_ext::container::store::PrepareResult; use ostree_ext::ostree; use ostree_ext::ostree::Deployment; use ostree_ext::sysroot::SysrootLock; +use ostree_ext::prelude::FileExt; +use ostree_ext::prelude::Cast; +use ostree_ext::prelude::FileEnumeratorExt; use crate::spec::HostSpec; use crate::spec::ImageReference; @@ -219,8 +222,10 @@ async fn deploy( stateroot: &str, image: &ImageState, origin: &glib::KeyFile, + opts: Option>, ) -> Result<()> { let stateroot = Some(stateroot); + let opts = opts.unwrap_or(Default::default()); // Copy to move into thread let cancellable = gio::Cancellable::NONE; let _new_deployment = sysroot.stage_tree_with_options( @@ -228,7 +233,7 @@ async fn deploy( image.ostree_commit.as_str(), Some(origin), merge_deployment, - &Default::default(), + &opts, cancellable, )?; Ok(()) @@ -253,6 +258,7 @@ pub(crate) async fn stage( stateroot: &str, image: &ImageState, spec: &RequiredHostSpec<'_>, + opts: Option>, ) -> Result<()> { let merge_deployment = sysroot.merge_deployment(Some(stateroot)); let origin = origin_from_imageref(spec.image)?; @@ -262,6 +268,7 @@ pub(crate) async fn stage( stateroot, image, &origin, + opts, ) .await?; crate::deploy::cleanup(sysroot).await?; @@ -340,6 +347,64 @@ pub(crate) fn switch_origin_inplace(root: &Dir, imgref: &ImageReference) -> Resu Ok(newest_deployment) } +pub fn get_kargs(repo: &ostree::Repo, fetched: &ImageState) -> Result> { + let cancellable = gio::Cancellable::NONE; + + // Get the running kernel commandline arguments + let kargs = ostree::KernelArgs::new(); + ostree::KernelArgs::append_proc_cmdline( + &kargs, + cancellable, + )?; + let mut kargs: Vec = kargs.to_strv().iter().map(|s| { s.as_str().to_string() }).collect(); + + // Get the kargs in kargs.d of the booted system + let mut existing_kargs = vec![]; + let fragments = liboverdrop::scan(&["/usr/lib"], "bootc/kargs.d", &["toml"], true); + for (_name, path) in fragments { + let buffer = std::fs::read_to_string(&path)?; + existing_kargs.push(buffer.trim().to_string()); + } + + // Get the kargs in kargs.d of the remote image + let mut remote_kargs = vec![]; + let (fetched_tree, _) = repo.read_commit(fetched.ostree_commit.as_str(), cancellable)?; + let fetched_tree = fetched_tree.resolve_relative_path("/usr/lib/bootc/kargs.d"); + let fetched_tree = fetched_tree.downcast::().expect("downcast"); + match fetched_tree.query_exists(cancellable) { + true => {} + false => { + return Ok(vec![]); + } + } + let queryattrs = "standard::name,standard::type"; + let queryflags = gio::FileQueryInfoFlags::NOFOLLOW_SYMLINKS; + let fetched_iter = fetched_tree.enumerate_children(queryattrs, queryflags, cancellable)?; + while let Some(fetched_info) = fetched_iter.next_file(cancellable)? { + let fetched_child = fetched_iter.child(&fetched_info); + let fetched_child = fetched_child.downcast::().expect("downcast"); + fetched_child.ensure_resolved()?; + let fetched_contents_checksum = fetched_child.checksum(); + let f = ostree::Repo::load_file(repo, fetched_contents_checksum.as_str(), cancellable)?; + let file_content = f.0; + let mut buffer = vec![]; + let mut reader = ostree_ext::prelude::InputStreamExtManual::into_read(file_content.unwrap()); + let _ = std::io::Read::read_to_end(&mut reader, &mut buffer); + let s = std::string::String::from_utf8(buffer)?; + remote_kargs.push(s.trim().to_string()); + } + + // get the diff between the existing and remote kargs + let mut added_kargs: Vec = remote_kargs.clone().into_iter().filter(|item| !existing_kargs.contains(item)).collect(); + let removed_kargs: Vec = existing_kargs.clone().into_iter().filter(|item| !remote_kargs.contains(item)).collect(); + + // apply the diff to the system kargs + kargs.retain(|x| !removed_kargs.contains(x)); + kargs.append(&mut added_kargs); + + Ok(kargs) +} + #[test] fn test_switch_inplace() -> Result<()> { use cap_std::fs::DirBuilderExt;