app/dbus-helpers: Don't error out if caller is updates driver
If the systemd unit associated with the client's PID is the updates driver's unit, don't require the --bypass-driver option for operations like upgrade, deploy, and rebase. This is useful for updates drivers that shell out to rpm-ostree's binary (e.g. Zincati, currently). Also refactor some helper functions to make them more general and reusable.
This commit is contained in:
parent
705b22df28
commit
1c826e993b
@ -287,8 +287,8 @@ get_update_driver_state (RPMOSTreeSysroot *sysroot_proxy,
|
||||
g_dbus_proxy_get_connection (G_DBUS_PROXY (sysroot_proxy));
|
||||
|
||||
const char *update_driver_objpath = NULL;
|
||||
if (!get_sd_unit_objpath(connection, update_driver_sd_unit, &update_driver_objpath,
|
||||
cancellable, error))
|
||||
if (!get_sd_unit_objpath (connection, "LoadUnit", g_variant_new ("(s)", update_driver_sd_unit),
|
||||
&update_driver_objpath, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* Look up ActiveState property of update driver's systemd unit. */
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <sys/socket.h>
|
||||
#include <systemd/sd-login.h>
|
||||
#include <utility>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <glib-unix.h>
|
||||
#include <libglnx.h>
|
||||
@ -1526,47 +1527,45 @@ rpmostree_print_cached_update (GVariant *cached_update,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Query systemd for update driver's systemd unit's object path. */
|
||||
/* Query systemd for systemd unit's object path using method_name provided with
|
||||
* parameters. The reply_type of method_name must be G_VARIANT_TYPE_TUPLE. */
|
||||
gboolean
|
||||
get_sd_unit_objpath (GDBusConnection *connection,
|
||||
const char *update_driver_sd_unit,
|
||||
const char **update_driver_objpath,
|
||||
const char *method_name,
|
||||
GVariant *parameters,
|
||||
const char **unit_objpath,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GVariant) update_driver_objpath_tuple =
|
||||
g_dbus_connection_call_sync (connection, "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
|
||||
"org.freedesktop.systemd1.Manager", "LoadUnit",
|
||||
g_variant_new ("(s)", update_driver_sd_unit), G_VARIANT_TYPE_TUPLE,
|
||||
"org.freedesktop.systemd1.Manager", method_name,
|
||||
parameters, G_VARIANT_TYPE_TUPLE,
|
||||
G_DBUS_CALL_FLAGS_NONE, -1, cancellable, error);
|
||||
if (!update_driver_objpath_tuple)
|
||||
return FALSE;
|
||||
else if (g_variant_n_children (update_driver_objpath_tuple) < 1)
|
||||
return glnx_throw (error, "LoadUnit(%s) returned empty tuple", update_driver_sd_unit);
|
||||
return glnx_throw (error, "%s returned empty tuple", method_name);
|
||||
|
||||
g_autoptr(GVariant) update_driver_objpath_val =
|
||||
g_variant_get_child_value (update_driver_objpath_tuple, 0);
|
||||
*update_driver_objpath = g_variant_dup_string (update_driver_objpath_val, NULL);
|
||||
g_assert (*update_driver_objpath);
|
||||
*unit_objpath = g_variant_dup_string (update_driver_objpath_val, NULL);
|
||||
g_assert (*unit_objpath);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Get the `Documentation` property of `sd_unit`. Documentation is returned
|
||||
* as a string array GVariant in `unit_doc_array`. */
|
||||
/* Get the property_name property of sd_unit. Returns a reference to the GVariant
|
||||
* instance that holds the value for property_name GVariant in cache_val. */
|
||||
static gboolean
|
||||
get_sd_unit_doc (GDBusConnection *connection,
|
||||
const char *sd_unit,
|
||||
GVariant **unit_doc_array,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
get_sd_unit_property (GDBusConnection *connection,
|
||||
const char *objpath,
|
||||
const char *property_name,
|
||||
GVariant **cache_val,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
const char *objpath = NULL;
|
||||
if (!get_sd_unit_objpath (connection, sd_unit, &objpath,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* Look up `Documentation` property of systemd unit. */
|
||||
/* Look up property_name property of systemd unit. */
|
||||
g_autoptr(GDBusProxy) unit_obj_proxy =
|
||||
g_dbus_proxy_new_sync (connection, G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, NULL,
|
||||
"org.freedesktop.systemd1", objpath,
|
||||
@ -1574,11 +1573,11 @@ get_sd_unit_doc (GDBusConnection *connection,
|
||||
if (!unit_obj_proxy)
|
||||
return FALSE;
|
||||
|
||||
*unit_doc_array =
|
||||
g_dbus_proxy_get_cached_property (unit_obj_proxy, "Documentation");
|
||||
if (!*unit_doc_array)
|
||||
return glnx_throw (error, "Documentation property not found in proxy's cache (%s)",
|
||||
objpath);
|
||||
*cache_val =
|
||||
g_dbus_proxy_get_cached_property (unit_obj_proxy, property_name);
|
||||
if (!*cache_val)
|
||||
return glnx_throw (error, "%s property not found in proxy's cache (%s)",
|
||||
property_name, objpath);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@ -1587,22 +1586,22 @@ get_sd_unit_doc (GDBusConnection *connection,
|
||||
* Prints any errors that occur. */
|
||||
static void
|
||||
append_docs_to_str (GString *str,
|
||||
RPMOSTreeSysroot *sysroot_proxy,
|
||||
char *name,
|
||||
GDBusConnection *connection,
|
||||
char *sd_unit,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
g_autoptr(GVariant) docs_array = NULL;
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
GDBusConnection *connection =
|
||||
g_dbus_proxy_get_connection (G_DBUS_PROXY (sysroot_proxy));
|
||||
if (!get_sd_unit_doc (connection, sd_unit, &docs_array, cancellable, &local_error))
|
||||
const char *objpath = NULL;
|
||||
if (!get_sd_unit_objpath (connection, "LoadUnit", g_variant_new ("(s)", sd_unit),
|
||||
&objpath, cancellable, &local_error))
|
||||
g_printerr ("%s", local_error->message);
|
||||
if (!get_sd_unit_property (connection, objpath, "Documentation", &docs_array, cancellable, &local_error))
|
||||
{
|
||||
g_printerr ("%s", local_error->message);
|
||||
}
|
||||
else if (docs_array)
|
||||
{
|
||||
g_string_append_printf (str, "See %s's documentation", name);
|
||||
gsize docs_len;
|
||||
g_autofree const char **docs =
|
||||
g_variant_get_strv (docs_array, &docs_len);
|
||||
@ -1620,6 +1619,32 @@ append_docs_to_str (GString *str,
|
||||
}
|
||||
}
|
||||
|
||||
/* Check whether sd_unit contains pid and return the boolean in sd_unit_contains_pid */
|
||||
static gboolean
|
||||
check_sd_unit_contains_pid (char *sd_unit,
|
||||
pid_t pid,
|
||||
gboolean *sd_unit_contains_pid,
|
||||
GDBusConnection *connection,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
// Get the systemd unit associated with pid.
|
||||
const char *objpath = NULL;
|
||||
g_autoptr(GVariant) process_sd_unit_val = NULL;
|
||||
const char *process_sd_unit = NULL;
|
||||
if (!get_sd_unit_objpath (connection, "GetUnitByPID", g_variant_new ("(u)", (guint32) pid),
|
||||
&objpath, cancellable, error))
|
||||
return FALSE;
|
||||
if (!get_sd_unit_property (connection, objpath, "Id", &process_sd_unit_val, cancellable, error))
|
||||
return FALSE;
|
||||
process_sd_unit = g_variant_get_string (process_sd_unit_val, NULL);
|
||||
if (g_strcmp0 (process_sd_unit, sd_unit) == 0)
|
||||
*sd_unit_contains_pid = TRUE;
|
||||
else
|
||||
*sd_unit_contains_pid = FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Throw an error if an updates driver is registered. */
|
||||
gboolean
|
||||
error_if_driver_registered (GBusType bus_type,
|
||||
@ -1636,13 +1661,26 @@ error_if_driver_registered (GBusType bus_type,
|
||||
* done through the driver. */
|
||||
if (update_driver_sd_unit && update_driver_name)
|
||||
{
|
||||
GDBusConnection *connection =
|
||||
g_dbus_proxy_get_connection (G_DBUS_PROXY (sysroot_proxy));
|
||||
|
||||
// Do not error out if current process' systemd unit is the same as updates driver's.
|
||||
pid_t pid = getpid();
|
||||
gboolean sd_unit_contains_pid = FALSE;
|
||||
if (!check_sd_unit_contains_pid (update_driver_sd_unit, pid, &sd_unit_contains_pid,
|
||||
connection, cancellable, error))
|
||||
return FALSE;
|
||||
if (sd_unit_contains_pid)
|
||||
return TRUE;
|
||||
|
||||
// Build and throw error message.
|
||||
g_autoptr(GString) error_msg = g_string_new(NULL);
|
||||
g_string_printf (error_msg, "Updates and deployments are driven by %s (%s)\n",
|
||||
update_driver_name, update_driver_sd_unit);
|
||||
g_string_append_printf (error_msg, "See %s's documentation", update_driver_name);
|
||||
/* only try to get unit's `Documentation` if we're on the system bus */
|
||||
if (bus_type == G_BUS_TYPE_SYSTEM)
|
||||
append_docs_to_str (error_msg, sysroot_proxy, update_driver_name,
|
||||
update_driver_sd_unit, cancellable);
|
||||
append_docs_to_str (error_msg, connection, update_driver_sd_unit, cancellable);
|
||||
g_string_append_printf (error_msg,
|
||||
"Use --bypass-driver to bypass %s and perform the operation anyways",
|
||||
update_driver_name);
|
||||
|
@ -133,8 +133,9 @@ rpmostree_print_cached_update (GVariant *cached_update,
|
||||
|
||||
gboolean
|
||||
get_sd_unit_objpath (GDBusConnection *connection,
|
||||
const char *update_driver_sd_unit,
|
||||
const char **update_driver_objpath,
|
||||
const char *method_name,
|
||||
GVariant *parameters,
|
||||
const char **unit_objpath,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
|
@ -289,16 +289,18 @@ assert_file_has_content status.txt "failed to finalize previous deployment"
|
||||
assert_file_has_content status.txt "error: opendir"
|
||||
echo "ok previous staged failure in status"
|
||||
|
||||
# Test `deploy --register-driver` option.
|
||||
vm_cmd rpm-ostree deploy \'\' \
|
||||
# Test `deploy --register-driver` option
|
||||
# Create and start a transient test-driver.service unit to register our fake driver
|
||||
vm_cmd systemd-run --unit=test-driver.service --wait -q \
|
||||
rpm-ostree deploy \'\' \
|
||||
--register-driver=TestDriver
|
||||
vm_cmd test -f /run/rpm-ostree/update-driver.gv
|
||||
vm_cmd rpm-ostree status > status.txt
|
||||
assert_file_has_content status.txt 'AutomaticUpdatesDriver: TestDriver'
|
||||
vm_cmd rpm-ostree status -v > verbose_status.txt
|
||||
assert_file_has_content verbose_status.txt 'AutomaticUpdatesDriver: TestDriver (sshd.service)'
|
||||
assert_file_has_content verbose_status.txt 'AutomaticUpdatesDriver: TestDriver (test-driver.service)'
|
||||
vm_assert_status_jq ".\"update-driver\"[\"driver-name\"] == \"TestDriver\"" \
|
||||
".\"update-driver\"[\"driver-sd-unit\"] == \"sshd.service\""
|
||||
".\"update-driver\"[\"driver-sd-unit\"] == \"test-driver.service\""
|
||||
echo "ok deploy --register-driver with empty string revision"
|
||||
|
||||
# Ensure that we are prevented from rebasing when an updates driver is registered
|
||||
@ -333,3 +335,15 @@ vm_rpmostree upgrade --bypass-driver 2>err.txt
|
||||
assert_not_file_has_content err.txt 'Updates and deployments are driven by TestDriver'
|
||||
vm_rpmostree cleanup -p
|
||||
echo "ok upgrade when updates driver is registered"
|
||||
|
||||
# Test that we don't need to --bypass-driver if the systemd unit associated with
|
||||
# the client's PID is the update driver's systemd unit.
|
||||
vm_cmd rpm-ostree deploy \'\' \
|
||||
--register-driver=OtherTestDriver --bypass-driver
|
||||
# Make sure OtherTestDriver's systemd unit will be the same as the commandline's
|
||||
vm_cmd rpm-ostree status -v > verbose_status.txt
|
||||
assert_file_has_content verbose_status.txt 'AutomaticUpdatesDriver: OtherTestDriver (sshd.service)'
|
||||
vm_rpmostree upgrade 2>err.txt
|
||||
assert_not_file_has_content err.txt 'Updates and deployments are driven by OtherTestDriver'
|
||||
vm_rpmostree cleanup -p
|
||||
echo "ok upgrade without --bypass-driver when same systemd unit"
|
||||
|
Loading…
Reference in New Issue
Block a user