diff --git a/Makefile-rpm-ostree.am b/Makefile-rpm-ostree.am index 7bc419e405..7a1c2f70d0 100644 --- a/Makefile-rpm-ostree.am +++ b/Makefile-rpm-ostree.am @@ -29,6 +29,7 @@ rpm_ostree_SOURCES = src/app/main.c \ src/app/rpmostree-builtin-reload.c \ src/app/rpmostree-builtin-rebase.c \ src/app/rpmostree-builtin-cancel.c \ + src/app/rpmostree-builtin-cliwrap.c \ src/app/rpmostree-builtin-cleanup.c \ src/app/rpmostree-builtin-initramfs.c \ src/app/rpmostree-builtin-livefs.c \ @@ -104,7 +105,7 @@ librpmostree_rust_path = @abs_top_builddir@/target/@RUST_TARGET_SUBDIR@/librpmos # If the target directory exists, and isn't owned by our uid, then # we exit with a fatal error, since someone probably did `make && sudo make install`, # and in this case cargo will download into ~/.root which we don't want. -LIBRPMOSTREE_RUST_SRCS = $(wildcard rust/src/*.rs) rust/cbindgen.toml +LIBRPMOSTREE_RUST_SRCS = $(shell find rust/src/ -name '*.rs') rust/cbindgen.toml $(librpmostree_rust_path): Makefile $(LIBRPMOSTREE_RUST_SRCS) cd $(top_srcdir)/rust && \ export CARGO_TARGET_DIR=@abs_top_builddir@/target && \ diff --git a/docs/manual/treefile.md b/docs/manual/treefile.md index 18017e73d9..fe2e1ecc17 100644 --- a/docs/manual/treefile.md +++ b/docs/manual/treefile.md @@ -77,6 +77,10 @@ It supports the following parameters: specific filesystem drivers are included. If not specified, `--no-hostonly` will be used. + * `cliwrap`: boolean, optional. Defaults to `true`. If enabled, + rpm-ostree will replace binaries such as `/usr/bin/rpm` with + wrappers that intercept unsafe operations, or adjust functionality. + * `remove-files`: Array of files to delete from the generated tree. * `remove-from-packages`: Array, optional: Delete from specified packages diff --git a/rust/Cargo.lock b/rust/Cargo.lock index ef23c925f7..84a5214e5e 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -232,7 +232,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.28 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -388,6 +388,18 @@ name = "memoffset" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "nix" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "nodrop" version = "0.1.13" @@ -638,6 +650,7 @@ dependencies = [ "indicatif 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "openat 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -706,7 +719,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.28 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -758,7 +771,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" -version = "0.15.27" +version = "0.15.28" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", @@ -773,7 +786,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.28 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -876,6 +889,11 @@ name = "version_check" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi" version = "0.2.8" @@ -959,6 +977,7 @@ dependencies = [ "checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" +"checksum nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46f0f3210768d796e8fa79ec70ee6af172dacbe7147f5e69be5240a47778302b" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" @@ -1002,7 +1021,7 @@ dependencies = [ "checksum socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d11a52082057d87cb5caa31ad812f4504b97ab44732cd8359df2e9ff9f48e7" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" -"checksum syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)" = "525bd55255f03c816e5d7f615587bd13030c7103354fadb104993dcee6a788ec" +"checksum syn 0.15.28 (registry+https://github.com/rust-lang/crates.io-index)" = "218aa5a01ab9805df6e9e48074c8d88f317cc9660b1ad6c3dabac2d627d185d6" "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" "checksum systemd 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "daf2fd2d346d2dfb5fdc5f47b355f60dcfc35f5ee3e89e64e7ae2849ec8792a5" "checksum tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b86c784c88d98c801132806dadd3819ed29d8600836c4088e855cdf3e178ed8a" @@ -1018,6 +1037,7 @@ dependencies = [ "checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" +"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index a158f63ae7..9597cd6a1c 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = ["Colin Walters ", "Jonathan Lebon "] [dependencies] +nix = "0.13.0" failure = "0.1.3" serde = "1.0.78" serde_derive = "1.0.78" diff --git a/rust/src/cliwrap.rs b/rust/src/cliwrap.rs new file mode 100644 index 0000000000..020474d806 --- /dev/null +++ b/rust/src/cliwrap.rs @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2019 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +use std::io::prelude::*; +use std::{io, path}; +use failure::Fallible; + +use rayon::prelude::*; +use openat_utils::OpenatDirExt; +mod cliutil; +mod rpm; +mod grubby; +mod dracut; + +/// Location for the underlying (not wrapped) binaries. +pub(crate) static WRAP_DESTDIR : &str = "usr/libexec/rpm-ostree/wrapped"; + +/// Our list of binaries that will be wrapped. Must be a relative path. +static WRAPPED_BINARIES : &[&str] = &["usr/bin/rpm", + "usr/bin/dracut", + "usr/sbin/grubby"]; + +/// Main entrypoint for cliwrap +fn cliwrap_main(args: &Vec) -> Fallible<()> { + // We'll panic here if the vector is empty, but that is intentional; + // the outer code should always pass us at least one arg. + let name = args[0].as_str(); + let args : Vec<&str> = args.iter().map(|v| v.as_str()).collect(); + + // If we're not booted into ostree, just run the child directly. + if !cliutil::is_ostree_booted() { + cliutil::exec_real_wrapped(name, &args) + } else { + match name { + "rpm" => self::rpm::main(&args), + "dracut" => self::dracut::main(&args), + "grubby" => self::grubby::main(&args), + _ => bail!("Unknown wrapped binary: {}", name), + } + } +} + +/// Move the real binaries to a subdir, and replace them with +/// a shell script that calls our wrapping code. +fn write_wrappers(rootfs_dfd: &openat::Dir) -> Fallible<()> { + rootfs_dfd.create_dir(WRAP_DESTDIR, 0o755)?; + WRAPPED_BINARIES.par_iter().try_for_each(|&bin| { + let binpath = path::Path::new(bin); + + if !rootfs_dfd.exists(binpath)? { + return Ok(()); + } + + let name = binpath.file_name().unwrap().to_str().unwrap(); + let destpath = format!("{}/{}", WRAP_DESTDIR, name); + rootfs_dfd.local_rename(bin, destpath.as_str())?; + + let f = rootfs_dfd.write_file(binpath, 0o755)?; + let mut f = io::BufWriter::new(f); + f.write(b"#!/bin/sh\nexec /usr/bin/rpm-ostree cliwrap $0\n")?; + f.flush()?; + Ok(()) + }) +} + +mod ffi { + use super::*; + use ffiutil::*; + use glib; + use libc; + use failure::ResultExt; + + #[no_mangle] + pub extern "C" fn ror_cliwrap_write_wrappers(rootfs_dfd: libc::c_int, gerror: *mut *mut glib_sys::GError) -> libc::c_int { + let rootfs_dfd = ffi_view_openat_dir(rootfs_dfd); + int_glib_error(write_wrappers(&rootfs_dfd).with_context(|e| format!("During cli wrapper replacement: {}", e)), gerror) + } + + #[no_mangle] + pub extern "C" fn ror_cliwrap_entrypoint(argv: *mut *mut libc::c_char, + gerror: *mut *mut glib_sys::GError) -> libc::c_int { + let v: Vec = unsafe { glib::translate::FromGlibPtrContainer::from_glib_none(argv) }; + int_glib_error(cliwrap_main(&v), gerror) + } +} +pub use self::ffi::*; diff --git a/rust/src/cliwrap/cliutil.rs b/rust/src/cliwrap/cliutil.rs new file mode 100644 index 0000000000..a19c18200b --- /dev/null +++ b/rust/src/cliwrap/cliutil.rs @@ -0,0 +1,104 @@ +use failure::Fallible; +use libc; +use std::{thread, time, path}; +use std::ffi::CString; +use nix::sys::statvfs; +use nix::unistd; + +use cliwrap; + +/// Returns true if the current process is booted via ostree. +pub fn is_ostree_booted() -> bool { + path::Path::new("/run/ostree-booted").exists() +} + +/// Returns true if the current process is running as root. +pub fn is_unlocked() -> Fallible { + Ok(!statvfs::statvfs("/usr")?.flags().contains(statvfs::FsFlags::ST_RDONLY)) +} + +/// Returns true if the current process is running as root. +pub fn am_privileged() -> bool { + unsafe { libc::getuid() == 0 } +} + +/// Wrapper for execv which accepts strings +fn execvp_strs(argv0: &str, argv: &[&str]) -> Fallible<()> { + let argv0 = CString::new(argv0).unwrap(); + let argv : Vec = argv.iter().map(|&v| CString::new(v).unwrap()).collect(); + unistd::execvp(&argv0, &argv)?; + Ok(()) +} + +/// Return the absolute path to the underlying wrapped binary +fn get_real_bin(bin_name: &str) -> String { + format!("/{}/{}", cliwrap::WRAP_DESTDIR, bin_name) +} + +/// Wrapper for execv which accepts strings +pub fn exec_real_wrapped + std::fmt::Display>(bin_name: T, argv: &[T]) -> Fallible<()> { + let bin_name = bin_name.as_ref(); + let real_bin = get_real_bin(bin_name); + let argv : Vec<&str> = std::iter::once(bin_name).chain(argv.iter().map(|v| v.as_ref())).collect(); + execvp_strs(real_bin.as_str(), &argv) +} + +/// Run a subprocess synchronously as user `bin` (dropping all capabilities). +pub fn run_unprivileged>( + with_warning: bool, + target_bin: &str, + argv: &[T], +) -> Fallible<()> { + // `setpriv` is in util-linux; we could do this internally, but this is easier. + let setpriv_argv = &[ + "setpriv", + "--no-new-privs", + "--reuid=bin", + "--regid=bin", + "--init-groups", + "--bounding-set", + "-all", + "--", + ]; + + let argv: Vec<&str> = argv.into_iter().map(AsRef::as_ref).collect(); + let drop_privileges = am_privileged (); + let app_name = "rpm-ostree"; + if with_warning { + let delay_s = 5; + eprintln!( + "{name}: NOTE: This system is ostree based.", + name = app_name + ); + if drop_privileges { + eprintln!(r#"{name}: Dropping privileges as `{bin}` was executed with not "known safe" arguments."#, + name=app_name, bin = target_bin); + } else { + eprintln!( + r#"{name}: Wrapped binary "{bin}" was executed with not "known safe" arguments."#, + name = app_name, + bin = target_bin + ); + } + eprintln!( + r##"{name}: You may invoke the real `{bin}` binary in `/{wrap_destdir}/{bin}`. +{name}: Continuing execution in {delay} seconds. +"##, + name = app_name, + wrap_destdir = cliwrap::WRAP_DESTDIR, + bin = target_bin, + delay = delay_s, + ); + thread::sleep(time::Duration::from_secs(delay_s)); + } + + if drop_privileges { + let real_bin = get_real_bin(target_bin); + let real_argv : Vec<&str> = setpriv_argv.iter().map(|&v| v) + .chain(std::iter::once(real_bin.as_str())) + .chain(argv).collect(); + execvp_strs("setpriv", &real_argv) + } else { + exec_real_wrapped(target_bin, &argv) + } +} diff --git a/rust/src/cliwrap/dracut.rs b/rust/src/cliwrap/dracut.rs new file mode 100644 index 0000000000..ffc319b44e --- /dev/null +++ b/rust/src/cliwrap/dracut.rs @@ -0,0 +1,9 @@ +use failure::Fallible; + +/// Primary entrypoint to running our wrapped `dracut` handling. +pub(crate) fn main(_argv: &[&str]) -> Fallible<()> { + eprintln!("This system is rpm-ostree based; initramfs handling is +integrated with the underlying ostree transaction mechanism. +Use `rpm-ostree initramfs` to control client-side initramfs generation."); + std::process::exit(1); +} diff --git a/rust/src/cliwrap/grubby.rs b/rust/src/cliwrap/grubby.rs new file mode 100644 index 0000000000..5384d8f9e1 --- /dev/null +++ b/rust/src/cliwrap/grubby.rs @@ -0,0 +1,8 @@ +use failure::Fallible; + +/// Primary entrypoint to running our wrapped `grubby` handling. +pub(crate) fn main(_argv: &[&str]) -> Fallible<()> { + eprintln!("This system is rpm-ostree based; grubby is not used. +Use `rpm-ostree kargs` instead."); + std::process::exit(1); +} diff --git a/rust/src/cliwrap/rpm.rs b/rust/src/cliwrap/rpm.rs new file mode 100644 index 0000000000..6a85007164 --- /dev/null +++ b/rust/src/cliwrap/rpm.rs @@ -0,0 +1,64 @@ +use clap::{App, Arg}; +use failure::Fallible; + +use crate::cliwrap::cliutil; + +/// Set of RPM arguments that we know are safe to pass through, +/// or that we handle internally. +fn new_rpm_app<'r>() -> App<'r, 'static> { + let name = "cli-ostree-wrapper-rpm"; + App::new(name) + .bin_name(name) + .version("0.1") + .about("Wrapper for rpm") + .arg(Arg::with_name("all").short("a")) + .arg(Arg::with_name("file").short("f")) + .arg(Arg::with_name("package").short("p")) + .arg(Arg::with_name("query").short("q")) + .arg(Arg::with_name("verify").short("V")) + .arg(Arg::with_name("version")) +} + +/// Primary entrypoint to running our wrapped `rpm` handling. +pub(crate) fn main(argv: &[&str]) -> Fallible<()> { + if cliutil::is_unlocked()? { + // For now if we're unlocked, just directly exec rpm. In the future we + // may choose to take over installing a package live. + cliutil::exec_real_wrapped("rpm", argv) + } else { + let mut app = new_rpm_app(); + let mut with_warning = true; + if let Ok(matches) = app.get_matches_from_safe_borrow(argv) { + // Implement custom option handling here + if matches.is_present("verify") { + println!("rpm --verify is not necessary for ostree-based systems. +All binaries in /usr are underneath a read-only bind mount. +If you wish to verify integrity, use `ostree fsck`."); + return Ok(()) + } + with_warning = false; + } + cliutil::run_unprivileged(with_warning, "rpm", argv) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_qa() { + let app = new_rpm_app(); + let argv = vec!["rpm", "-qa"]; + let matches = app.get_matches_from_safe(&argv); + assert!(matches.is_ok()); + } + + #[test] + fn test_unknown() { + let app = new_rpm_app(); + let argv = vec!["rpm", "--not-a-valid-arg"]; + let matches = app.get_matches_from_safe(&argv); + assert!(matches.is_err()); + } + +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs index af03118d21..49103e8a22 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -26,6 +26,7 @@ extern crate glib; extern crate glib_sys; extern crate indicatif; extern crate libc; +extern crate nix; extern crate openat; extern crate rayon; extern crate tempfile; @@ -40,6 +41,8 @@ extern crate serde_yaml; mod ffiutil; +mod cliwrap; +pub use cliwrap::*; mod treefile; pub use treefile::*; mod composepost; diff --git a/rust/src/openat_utils.rs b/rust/src/openat_utils.rs index 4680b8eec6..d8f6fbdcf5 100644 --- a/rust/src/openat_utils.rs +++ b/rust/src/openat_utils.rs @@ -30,6 +30,9 @@ pub(crate) trait OpenatDirExt { // On modern filesystems the directory entry contains the type; if available, // return it. Otherwise invoke stat(). fn get_file_type(&self, e: &openat::Entry) -> io::Result; + + // Returns true iff file exists. + fn exists(&self, p: P) -> io::Result; } impl OpenatDirExt for openat::Dir { @@ -53,4 +56,17 @@ impl OpenatDirExt for openat::Dir { Ok(self.metadata(e.file_name())?.simple_type()) } } + + fn exists(&self, p: P) -> io::Result { + match self.metadata(p) { + Ok(_) => Ok(true), + Err(e) => { + if e.kind() == io::ErrorKind::NotFound { + Ok(false) + } else { + Err(e) + } + } + } + } } diff --git a/rust/src/treefile.rs b/rust/src/treefile.rs index aafc2fe548..defbb12f4d 100644 --- a/rust/src/treefile.rs +++ b/rust/src/treefile.rs @@ -604,6 +604,9 @@ struct TreeComposeConfig { #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "initramfs-args")] initramfs_args: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + // Defaults to `true` + cliwrap: Option, // Tree layout options #[serde(skip_serializing_if = "Option::is_none")] @@ -1094,6 +1097,13 @@ mod ffi { .unwrap_or(ptr::null_mut()) } + #[no_mangle] + pub extern "C" fn ror_treefile_get_cliwrap(tf: *mut Treefile) -> bool { + assert!(!tf.is_null()); + let tf = unsafe { &mut *tf }; + tf.parsed.cliwrap.unwrap_or(true) + } + #[no_mangle] pub extern "C" fn ror_treefile_free(tf: *mut Treefile) { if tf.is_null() { diff --git a/src/app/main.c b/src/app/main.c index 0a22261927..4685eca15e 100644 --- a/src/app/main.c +++ b/src/app/main.c @@ -120,6 +120,8 @@ static RpmOstreeCommand commands[] = { RPM_OSTREE_BUILTIN_FLAG_REQUIRES_ROOT | RPM_OSTREE_BUILTIN_FLAG_HIDDEN, NULL, rpmostree_builtin_start_daemon }, + { "cliwrap", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD | RPM_OSTREE_BUILTIN_FLAG_HIDDEN, + NULL, rpmostree_builtin_cliwrap }, { NULL } }; diff --git a/src/app/rpmostree-builtin-cliwrap.c b/src/app/rpmostree-builtin-cliwrap.c new file mode 100644 index 0000000000..cbe835b57c --- /dev/null +++ b/src/app/rpmostree-builtin-cliwrap.c @@ -0,0 +1,50 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2019 Red Hat, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include + +#include "rpmostree-builtins.h" +#include "rpmostree-libbuiltin.h" +#include "rpmostree-rust.h" + +#include + +gboolean +rpmostree_builtin_cliwrap (int argc, + char **argv, + RpmOstreeCommandInvocation *invocation, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(GOptionContext) context = g_option_context_new (""); + + if (argc < 2) + return glnx_throw (error, "cliwrap: missing required subcommand"); + + g_autoptr(GPtrArray) args = g_ptr_array_new (); + for (int i = 1; i < argc; i++) + g_ptr_array_add (args, argv[i]); + g_ptr_array_add (args, NULL); + return ror_cliwrap_entrypoint ((char**)args->pdata, error); +} diff --git a/src/app/rpmostree-builtins.h b/src/app/rpmostree-builtins.h index 2fcfad3f16..f14c325e0f 100644 --- a/src/app/rpmostree-builtins.h +++ b/src/app/rpmostree-builtins.h @@ -31,6 +31,7 @@ G_BEGIN_DECLS GCancellable *cancellable, GError **error) BUILTINPROTO(compose); +BUILTINPROTO(cliwrap); BUILTINPROTO(upgrade); BUILTINPROTO(reload); BUILTINPROTO(usroverlay); diff --git a/src/libpriv/rpmostree-core.c b/src/libpriv/rpmostree-core.c index 960f33f532..7e4640f7bc 100644 --- a/src/libpriv/rpmostree-core.c +++ b/src/libpriv/rpmostree-core.c @@ -4181,6 +4181,12 @@ rpmostree_context_assemble (RpmOstreeContext *self, return FALSE; } + if (ror_treefile_get_cliwrap (self->treefile_rs)) + { + if (!ror_cliwrap_write_wrappers (tmprootfs_dfd, error)) + return FALSE; + } + /* Undo the /etc move above */ if (renamed_etc) {