diff --git a/rust/src/cxxrsutil.rs b/rust/src/cxxrsutil.rs index dcee49d2..4daddfbe 100644 --- a/rust/src/cxxrsutil.rs +++ b/rust/src/cxxrsutil.rs @@ -95,8 +95,8 @@ macro_rules! cxxrs_bind { // When extending this list, also update rpmostree-cxxrs-prelude.h and lib.rs // This macro is special to ostree types currently. cxxrs_bind!(Ostree, ostree, ostree_sys, [Sysroot, Repo, Deployment]); -cxxrs_bind!(G, gio, gio_sys, [Cancellable]); cxxrs_bind!(G, glib, gobject_sys, [Object]); +cxxrs_bind!(G, gio, gio_sys, [Cancellable, DBusConnection]); cxxrs_bind!(G, glib, glib_sys, [VariantDict]); // An error type helper; separate from the GObject bridging diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 7c4255bf..f2fef4f8 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -37,6 +37,7 @@ pub mod ffi { type OstreeDeployment = crate::FFIOstreeDeployment; type GObject = crate::FFIGObject; type GCancellable = crate::FFIGCancellable; + type GDBusConnection = crate::FFIGDBusConnection; type GVariantDict = crate::FFIGVariantDict; #[namespace = "dnfcxx"] @@ -295,6 +296,14 @@ pub mod ffi { fn main_print_error(msg: &str); } + unsafe extern "C++" { + include!("rpmostree-clientlib.h"); + fn client_require_root() -> Result<()>; + type ClientConnection; + fn new_client_connection() -> Result>; + fn get_connection<'a>(self: Pin<&'a mut ClientConnection>) -> Pin<&'a mut GDBusConnection>; + } + unsafe extern "C++" { include!("rpmostree-diff.hpp"); type RPMDiff; diff --git a/rust/src/testutils.rs b/rust/src/testutils.rs index 2ad0eedc..bbc1d8a8 100644 --- a/rust/src/testutils.rs +++ b/rust/src/testutils.rs @@ -47,6 +47,8 @@ enum Opt { GenerateSyntheticUpgrade(SyntheticUpgradeOpts), /// Validate that we can parse the output of `rpm-ostree status --json`. ValidateParseStatus, + /// Test that we can 🐄 + Moo, } /// Returns `true` if a file is ELF; see https://en.wikipedia.org/wiki/Executable_and_Linkable_Format @@ -226,11 +228,54 @@ fn validate_parse_status() -> Result<()> { Ok(()) } +fn test_moo() -> Result<()> { + use glib::translate::*; + + crate::ffi::client_require_root()?; + + let mut client_conn = crate::ffi::new_client_connection()?; + let mut bus_conn = client_conn.pin_mut().get_connection(); + let bus_conn = bus_conn.gobj_wrap(); + + // Unfortunately glib bindings don't support tuples, we need + // `(b)` i.e. the 1-tuple with a boolean, and not just `b`. + let params = unsafe { + let truev = glib_sys::g_variant_new_boolean(true.to_glib()); + let r = glib_sys::g_variant_new_tuple(&truev as *const *mut _, 1); + glib_sys::g_variant_ref_sink(r); + from_glib_full(r) + }; + let reply = bus_conn.call_sync( + Some("org.projectatomic.rpmostree1"), + "/org/projectatomic/rpmostree1/fedora_coreos", + "org.projectatomic.rpmostree1.OSExperimental", + "Moo", + Some(¶ms), + Some(glib::VariantTy::new("(s)").unwrap()), + gio::DBusCallFlags::NONE, + -1, + gio::NONE_CANCELLABLE, + )?; + let reply_child: glib::Variant = unsafe { + from_glib_full(glib_sys::g_variant_get_child_value( + reply.to_glib_none().0, + 0, + )) + }; + // Unwrap safety: We validated the (s) above. + let reply = reply_child.get_str().unwrap(); + let cow = "🐄\n"; + assert_eq!(reply, cow); + println!("ok {}", cow.trim()); + Ok(()) +} + pub(crate) fn testutils_entrypoint(args: Vec) -> CxxResult<()> { let opt = Opt::from_iter(args.iter()); match opt { Opt::GenerateSyntheticUpgrade(ref opts) => update_os_tree(opts)?, Opt::ValidateParseStatus => validate_parse_status()?, + Opt::Moo => test_moo()?, }; Ok(()) } diff --git a/src/app/libmain.cxx b/src/app/libmain.cxx index 90ad63e8..ba8f487d 100644 --- a/src/app/libmain.cxx +++ b/src/app/libmain.cxx @@ -192,6 +192,17 @@ option_context_new_with_commands (RpmOstreeCommandInvocation *invocation, return util::move_nullify (context); } +namespace rpmostreecxx { + +void +client_require_root(void) +{ + if (getuid () != 0 && getenv ("RPMOSTREE_SUPPRESS_REQUIRES_ROOT_CHECK") == NULL) + throw std::runtime_error("This command requires root privileges"); +} + +} /* namespace */ + gboolean rpmostree_option_context_parse (GOptionContext *context, const GOptionEntry *main_entries, @@ -256,10 +267,8 @@ rpmostree_option_context_parse (GOptionContext *context, exit (EXIT_SUCCESS); } - if ((flags & RPM_OSTREE_BUILTIN_FLAG_REQUIRES_ROOT) > 0 - && getuid () != 0 - && getenv ("RPMOSTREE_SUPPRESS_REQUIRES_ROOT_CHECK") == NULL) - return glnx_throw (error, "This command requires root privileges"); + if ((flags & RPM_OSTREE_BUILTIN_FLAG_REQUIRES_ROOT) > 0) + rpmostreecxx::client_require_root(); if (use_daemon) { diff --git a/src/app/rpmostree-builtins.h b/src/app/rpmostree-builtins.h index de23e2b9..5b6ae003 100644 --- a/src/app/rpmostree-builtins.h +++ b/src/app/rpmostree-builtins.h @@ -23,6 +23,7 @@ #include "ostree.h" #include "rpmostree-builtin-types.h" #include "rpmostree-clientlib.h" +#include "rpmostree-cxxrs.h" G_BEGIN_DECLS diff --git a/src/app/rpmostree-clientlib.cxx b/src/app/rpmostree-clientlib.cxx index 46678a18..a0e938ad 100644 --- a/src/app/rpmostree-clientlib.cxx +++ b/src/app/rpmostree-clientlib.cxx @@ -240,6 +240,22 @@ app_load_sysroot_impl (const char *sysroot, gboolean force_peer, return TRUE; } +namespace rpmostreecxx { + +std::unique_ptr +new_client_connection() +{ + g_autoptr(GError) local_error = NULL; + GDBusConnection *conn = NULL; + GBusType bus_type; + + if (!app_load_sysroot_impl(NULL, false, NULL, &conn, &bus_type, &local_error)) + util::throw_gerror(local_error); + return std::make_unique(conn, bus_type); +} + +} // namespace + /** * rpmostree_load_sysroot * @sysroot: sysroot path diff --git a/src/app/rpmostree-clientlib.h b/src/app/rpmostree-clientlib.h index 81a47704..75554dc7 100644 --- a/src/app/rpmostree-clientlib.h +++ b/src/app/rpmostree-clientlib.h @@ -29,6 +29,35 @@ #include #include +#include + +namespace rpmostreecxx { + +class ClientConnection final { +private: + GDBusConnection *conn; + GBusType bus_type; +public: + ClientConnection(GDBusConnection *connp, GBusType bus_typep) : conn(connp), bus_type(bus_typep) {} + ~ClientConnection() { + g_clear_object(&conn); + } + + GDBusConnection &get_connection() { + return *conn; + } + + GBusType get_bus_type() const { + return bus_type; + } +}; + +void client_require_root(); + +std::unique_ptr new_client_connection(); + +} // namespace + G_BEGIN_DECLS #define BUS_NAME "org.projectatomic.rpmostree1" diff --git a/src/libpriv/rpmostree-cxxrs-prelude.h b/src/libpriv/rpmostree-cxxrs-prelude.h index d20b6adc..8436d163 100644 --- a/src/libpriv/rpmostree-cxxrs-prelude.h +++ b/src/libpriv/rpmostree-cxxrs-prelude.h @@ -28,8 +28,9 @@ namespace rpmostreecxx { typedef ::OstreeSysroot OstreeSysroot; typedef ::OstreeRepo OstreeRepo; typedef ::OstreeDeployment OstreeDeployment; - typedef ::GCancellable GCancellable; typedef ::GObject GObject; + typedef ::GCancellable GCancellable; + typedef ::GDBusConnection GDBusConnection; typedef ::GVariantDict GVariantDict; } diff --git a/tests/kolainst/nondestructive/misc.sh b/tests/kolainst/nondestructive/misc.sh index ce8aa320..9ccbd5e2 100755 --- a/tests/kolainst/nondestructive/misc.sh +++ b/tests/kolainst/nondestructive/misc.sh @@ -60,17 +60,8 @@ fi assert_file_has_content err.txt 'Unknown.*command' echo "ok error on unknown command" -stateroot=$(dirname $(ls /ostree/deploy/*/var)) -ospath=/org/projectatomic/rpmostree1/${stateroot//-/_} # related: https://github.com/coreos/fedora-coreos-config/issues/194 -(export LANG=C.utf8 - # And for some reason this one is set in kola runs but not interactive shells - unset LC_ALL - gdbus call \ - --system --dest org.projectatomic.rpmostree1 \ - --object-path /org/projectatomic/rpmostree1/fedora_coreos \ - --method org.projectatomic.rpmostree1.OSExperimental.Moo true > moo.txt - assert_file_has_content moo.txt '🐄') +rpm-ostree testutils moo echo "ok moo" # Reload as root https://github.com/projectatomic/rpm-ostree/issues/976