Add basic Rust CLI bindings for DBus

This stubs out sufficient infrastructure for us to register
as a client and call the Moo API.

A glaring problem here is the lack of extensive `glib::Variant`
bindings; that's covered in the next gtk-rs release.

My real goal was to try porting the `rpmostree-builtin-apply-live.cxx`
code entirely to Rust, but there's more to do to expose the
transaction helper APIs we have.
This commit is contained in:
Colin Walters 2021-02-27 19:50:12 +00:00 committed by OpenShift Merge Robot
parent 283824c6cb
commit 6fd9db246a
9 changed files with 117 additions and 16 deletions

View File

@ -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

View File

@ -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<UniquePtr<ClientConnection>>;
fn get_connection<'a>(self: Pin<&'a mut ClientConnection>) -> Pin<&'a mut GDBusConnection>;
}
unsafe extern "C++" {
include!("rpmostree-diff.hpp");
type RPMDiff;

View File

@ -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(&params),
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<String>) -> 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(())
}

View File

@ -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)
{

View File

@ -23,6 +23,7 @@
#include "ostree.h"
#include "rpmostree-builtin-types.h"
#include "rpmostree-clientlib.h"
#include "rpmostree-cxxrs.h"
G_BEGIN_DECLS

View File

@ -240,6 +240,22 @@ app_load_sysroot_impl (const char *sysroot, gboolean force_peer,
return TRUE;
}
namespace rpmostreecxx {
std::unique_ptr<ClientConnection>
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<ClientConnection>(conn, bus_type);
}
} // namespace
/**
* rpmostree_load_sysroot
* @sysroot: sysroot path

View File

@ -29,6 +29,35 @@
#include <string.h>
#include <ostree.h>
#include <memory>
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<ClientConnection> new_client_connection();
} // namespace
G_BEGIN_DECLS
#define BUS_NAME "org.projectatomic.rpmostree1"

View File

@ -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;
}

View File

@ -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