diff --git a/lib/src/cli.rs b/lib/src/cli.rs index 7d0fbea62..942cb2238 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,12 @@ 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 mut kargs: Vec<&str> = kargs.iter_mut().map(|s| { let s = s.trim(); s }).collect(); + kargs.sort(); + kargs.dedup(); + 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 +457,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 +471,12 @@ 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 mut kargs: Vec<&str> = kargs.iter_mut().map(|s| { let s = s.trim(); s }).collect(); + kargs.sort(); + kargs.dedup(); + opts.override_kernel_argv = Some(kargs.as_slice()); + crate::deploy::stage(sysroot, &stateroot, &fetched, &new_spec, Some(opts)).await?; Ok(()) } @@ -493,11 +505,18 @@ 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 mut kargs: Vec<&str> = kargs.iter_mut().map(|s| { let s = s.trim(); s }).collect(); + kargs.sort(); + kargs.dedup(); + 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..3326d2764 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,47 @@ 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; + 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 mut kargs = 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)?; + kargs.push(s); + } + + let existing_kargs = ostree::KernelArgs::new(); + ostree::KernelArgs::append_proc_cmdline( + &existing_kargs, + cancellable, + )?; + let mut existing_kargs = existing_kargs.to_strv().iter().map(|s| { s.as_str().to_string() }).collect(); + kargs.append(&mut existing_kargs); + + Ok(kargs) +} + #[test] fn test_switch_inplace() -> Result<()> { use cap_std::fs::DirBuilderExt;