rpm-ostree/src/daemon/rpmostreed-sysroot.c
Colin Walters 99901ac0dc daemon: Add an 'id' param to RegisterClient, log it
This makes the logs a bit more useful, but the ultimate goal
here is to write the originating client `id` to the cached update
data, so users know that e.g. `gnome-software` triggered it.

Closes: #1368
Approved by: jlebon
2018-05-16 18:11:40 +00:00

906 lines
30 KiB
C

/*
* Copyright (C) 2015 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
*/
#include "config.h"
#include "ostree.h"
#include "rpmostreed-daemon.h"
#include "rpmostreed-sysroot.h"
#include "rpmostreed-os.h"
#include "rpmostreed-os-experimental.h"
#include "rpmostree-util.h"
#include "rpmostreed-utils.h"
#include "rpmostreed-deployment-utils.h"
#include "rpmostreed-errors.h"
#include "rpmostreed-transaction.h"
#include "rpmostreed-transaction-monitor.h"
#include "rpmostree-output.h"
#include <err.h>
#include "libglnx.h"
#include <gio/gunixinputstream.h>
#include <gio/gunixoutputstream.h>
#include <systemd/sd-login.h>
#include <systemd/sd-journal.h>
static gboolean
sysroot_reload_ostree_configs_and_deployments (RpmostreedSysroot *self,
gboolean *out_changed,
GError **error);
/**
* SECTION: sysroot
* @title: RpmostreedSysroot
* @short_description: Implementation of #RPMOSTreeSysroot
*
* This type provides an implementation of the #RPMOSTreeSysroot interface.
*/
typedef struct _RpmostreedSysrootClass RpmostreedSysrootClass;
/**
* RpmostreedSysroot:
*
* The #RpmostreedSysroot structure contains only private data and should
* only be accessed using the provided API.
*/
struct _RpmostreedSysroot {
RPMOSTreeSysrootSkeleton parent_instance;
OstreeSysroot *ot_sysroot;
OstreeRepo *repo;
struct stat repo_last_stat;
RpmostreedTransactionMonitor *transaction_monitor;
PolkitAuthority *authority;
gboolean on_session_bus;
GHashTable *os_interfaces;
GHashTable *osexperimental_interfaces;
/* The OS interface's various diff methods can run concurrently with
* transactions, which is safe except when the transaction is writing
* new deployments to disk or downloading RPM package details. The
* writer lock protects these critical sections so the diff methods
* (holding reader locks) can run safely. */
GRWLock method_rw_lock;
GFileMonitor *monitor;
guint sig_changed;
};
struct _RpmostreedSysrootClass {
RPMOSTreeSysrootSkeletonClass parent_class;
};
enum {
UPDATED,
NUM_SIGNALS
};
static guint signals[NUM_SIGNALS];
static void rpmostreed_sysroot_iface_init (RPMOSTreeSysrootIface *iface);
G_DEFINE_TYPE_WITH_CODE (RpmostreedSysroot,
rpmostreed_sysroot,
RPMOSTREE_TYPE_SYSROOT_SKELETON,
G_IMPLEMENT_INTERFACE (RPMOSTREE_TYPE_SYSROOT,
rpmostreed_sysroot_iface_init));
static RpmostreedSysroot *_sysroot_instance;
/* ---------------------------------------------------------------------------------------------------- */
static void
sysroot_output_cb (RpmOstreeOutputType type, void *data, void *opaque)
{
RpmostreedSysroot *self = RPMOSTREED_SYSROOT (opaque);
glnx_unref_object RpmostreedTransaction *transaction = NULL;
gboolean output_to_self = FALSE;
transaction =
rpmostreed_transaction_monitor_ref_active_transaction (self->transaction_monitor);
if (transaction)
g_object_get (transaction, "output-to-self", &output_to_self, NULL);
if (!transaction || output_to_self)
{
rpmostree_output_default_handler (type, data, opaque);
return;
}
switch (type)
{
case RPMOSTREE_OUTPUT_MESSAGE:
rpmostree_transaction_emit_message (RPMOSTREE_TRANSACTION (transaction),
((RpmOstreeOutputMessage*)data)->text);
break;
case RPMOSTREE_OUTPUT_TASK_BEGIN:
rpmostree_transaction_emit_task_begin (RPMOSTREE_TRANSACTION (transaction),
((RpmOstreeOutputTaskBegin*)data)->text);
break;
case RPMOSTREE_OUTPUT_TASK_END:
rpmostree_transaction_emit_task_end (RPMOSTREE_TRANSACTION (transaction),
((RpmOstreeOutputTaskEnd*)data)->text);
break;
case RPMOSTREE_OUTPUT_PROGRESS_PERCENT:
rpmostree_transaction_emit_percent_progress (RPMOSTREE_TRANSACTION (transaction),
((RpmOstreeOutputProgressPercent*)data)->text,
((RpmOstreeOutputProgressPercent*)data)->percentage);
break;
case RPMOSTREE_OUTPUT_PROGRESS_N_ITEMS:
{
RpmOstreeOutputProgressNItems *nitems = data;
/* We still emit PercentProgress for compatibility with older clients as
* well as Cockpit. It's not worth trying to deal with version skew just
* for this yet.
*/
int percentage = (nitems->current == nitems->total) ? 100 :
(((double)(nitems->current)) / (nitems->total) * 100);
g_autofree char *newtext = g_strdup_printf ("%s (%u/%u)", nitems->text, nitems->current, nitems->total);
rpmostree_transaction_emit_percent_progress (RPMOSTREE_TRANSACTION (transaction), newtext, percentage);
}
break;
case RPMOSTREE_OUTPUT_PROGRESS_END:
rpmostree_transaction_emit_progress_end (RPMOSTREE_TRANSACTION (transaction));
break;
}
}
static gboolean
handle_create_osname (RPMOSTreeSysroot *object,
GDBusMethodInvocation *invocation,
const gchar *osname)
{
RpmostreedSysroot *self = RPMOSTREED_SYSROOT (object);
GError *error = NULL;
g_autofree gchar *dbus_path = NULL;
if (!ostree_sysroot_ensure_initialized (self->ot_sysroot, NULL, &error))
goto out;
if (strchr (osname, '/') != 0)
{
g_set_error_literal (&error,
RPM_OSTREED_ERROR,
RPM_OSTREED_ERROR_FAILED,
"Invalid osname");
goto out;
}
if (!ostree_sysroot_init_osname (self->ot_sysroot, osname, NULL, &error))
goto out;
dbus_path = rpmostreed_generate_object_path (BASE_DBUS_PATH, osname, NULL);
rpmostree_sysroot_complete_create_osname (RPMOSTREE_SYSROOT (self),
invocation,
g_strdup (dbus_path));
out:
if (error)
g_dbus_method_invocation_take_error (invocation, error);
return TRUE;
}
static gboolean
handle_get_os (RPMOSTreeSysroot *object,
GDBusMethodInvocation *invocation,
const char *arg_name)
{
RpmostreedSysroot *self = RPMOSTREED_SYSROOT (object);
if (arg_name[0] == '\0')
{
rpmostree_sysroot_complete_get_os (object,
invocation,
rpmostree_sysroot_dup_booted (object));
return FALSE;
}
g_autoptr(GDBusInterfaceSkeleton) os_interface =
g_hash_table_lookup (self->os_interfaces, arg_name);
if (os_interface != NULL)
g_object_ref (os_interface);
if (os_interface != NULL)
{
const char *object_path = g_dbus_interface_skeleton_get_object_path (os_interface);
rpmostree_sysroot_complete_get_os (object, invocation, object_path);
}
else
{
g_dbus_method_invocation_return_error (invocation,
G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
"OS name \"%s\" not found",
arg_name);
}
return TRUE;
}
static gboolean
sysroot_transform_transaction_to_attrs (GBinding *binding,
const GValue *src_value,
GValue *dst_value,
gpointer user_data)
{
RpmostreedTransaction *transaction;
GVariant *variant;
const char *method_name = "";
const char *path = "";
const char *sender_name = "";
transaction = g_value_get_object (src_value);
if (transaction != NULL)
{
GDBusMethodInvocation *invocation;
invocation = rpmostreed_transaction_get_invocation (transaction);
method_name = g_dbus_method_invocation_get_method_name (invocation);
path = g_dbus_method_invocation_get_object_path (invocation);
sender_name = g_dbus_method_invocation_get_sender (invocation);
}
variant = g_variant_new ("(sss)", method_name, sender_name, path);
g_value_set_variant (dst_value, variant);
return TRUE;
}
static gboolean
sysroot_transform_transaction_to_address (GBinding *binding,
const GValue *src_value,
GValue *dst_value,
gpointer user_data)
{
RpmostreedTransaction *transaction = g_value_get_object (src_value);
const char *address = "";
if (transaction != NULL)
address = rpmostreed_transaction_get_client_address (transaction);
g_value_set_string (dst_value, address);
return TRUE;
}
static gboolean
sysroot_populate_deployments_unlocked (RpmostreedSysroot *self,
gboolean *out_changed,
GError **error)
{
/* just set the out var early */
if (out_changed)
*out_changed = FALSE;
gboolean sysroot_changed;
if (!ostree_sysroot_load_if_changed (self->ot_sysroot, &sysroot_changed, NULL, error))
return FALSE;
struct stat repo_new_stat;
if (!glnx_fstat (ostree_repo_get_dfd (self->repo), &repo_new_stat, error))
return FALSE;
const gboolean repo_changed =
!((self->repo_last_stat.st_mtim.tv_sec == repo_new_stat.st_mtim.tv_sec) &&
(self->repo_last_stat.st_mtim.tv_nsec == repo_new_stat.st_mtim.tv_nsec));
if (repo_changed)
self->repo_last_stat = repo_new_stat;
if (!(sysroot_changed || repo_changed))
return TRUE; /* Note early return */
g_debug ("loading deployments");
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
g_autoptr(GHashTable) seen_osnames =
g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
/* Updated booted property; object owned by sysroot */
g_autofree gchar *booted_id = NULL;
OstreeDeployment *booted = ostree_sysroot_get_booted_deployment (self->ot_sysroot);
if (booted)
{
const gchar *os = ostree_deployment_get_osname (booted);
g_autofree gchar *path = rpmostreed_generate_object_path (BASE_DBUS_PATH, os, NULL);
rpmostree_sysroot_set_booted (RPMOSTREE_SYSROOT (self), path);
booted_id = rpmostreed_deployment_generate_id (booted);
}
else
{
rpmostree_sysroot_set_booted (RPMOSTREE_SYSROOT (self), "/");
}
/* Add deployment interfaces */
g_autoptr(GPtrArray) deployments = ostree_sysroot_get_deployments (self->ot_sysroot);
for (guint i = 0; deployments != NULL && i < deployments->len; i++)
{
OstreeDeployment *deployment = deployments->pdata[i];
GVariant *variant =
rpmostreed_deployment_generate_variant (self->ot_sysroot, deployment,
booted_id, self->repo, error);
if (!variant)
return glnx_prefix_error (error, "Reading deployment %u", i);
g_variant_builder_add_value (&builder, variant);
const char *deployment_os = ostree_deployment_get_osname (deployment);
/* Have we not seen this osname instance before? If so, add it
* now.
*/
if (!g_hash_table_contains (self->os_interfaces, deployment_os))
{
RPMOSTreeOS *obj = rpmostreed_os_new (self->ot_sysroot, self->repo,
deployment_os,
self->transaction_monitor);
g_hash_table_insert (self->os_interfaces, g_strdup (deployment_os), obj);
RPMOSTreeOSExperimental *eobj = rpmostreed_osexperimental_new (self->ot_sysroot, self->repo,
deployment_os,
self->transaction_monitor);
g_hash_table_insert (self->osexperimental_interfaces, g_strdup (deployment_os), eobj);
}
/* Owned by deployment, hash lifetime is smaller */
g_hash_table_add (seen_osnames, (char*)deployment_os);
}
/* Remove dead os paths */
GLNX_HASH_TABLE_FOREACH_IT (self->os_interfaces, it, const char*, k, GObject*, v)
{
if (!g_hash_table_contains (seen_osnames, k))
{
g_object_run_dispose (G_OBJECT (v));
g_hash_table_iter_remove (&it);
g_hash_table_remove (self->osexperimental_interfaces, k);
}
}
rpmostree_sysroot_set_deployments (RPMOSTREE_SYSROOT (self),
g_variant_builder_end (&builder));
g_debug ("finished deployments");
if (out_changed)
*out_changed = TRUE;
return TRUE;
}
static gboolean
handle_register_client (RPMOSTreeSysroot *object,
GDBusMethodInvocation *invocation,
GVariant *arg_options)
{
const char *sender = g_dbus_method_invocation_get_sender (invocation);
g_assert (sender);
g_autoptr(GVariantDict) optdict = g_variant_dict_new (arg_options);
const char *client_id = NULL;
g_variant_dict_lookup (optdict, "id", "&s", &client_id);
rpmostreed_daemon_add_client (rpmostreed_daemon_get (), sender, client_id);
rpmostree_sysroot_complete_register_client (object, invocation);
return TRUE;
}
static gboolean
handle_unregister_client (RPMOSTreeSysroot *object,
GDBusMethodInvocation *invocation,
GVariant *arg_options)
{
const char *sender;
sender = g_dbus_method_invocation_get_sender (invocation);
g_assert (sender);
rpmostreed_daemon_remove_client (rpmostreed_daemon_get (), sender);
rpmostree_sysroot_complete_unregister_client (object, invocation);
return TRUE;
}
/* remap relevant daemon configs to D-Bus properties */
static gboolean
reset_config_properties (RpmostreedSysroot *self,
GError **error)
{
RpmostreedDaemon *daemon = rpmostreed_daemon_get ();
RpmostreedAutomaticUpdatePolicy policy = rpmostreed_get_automatic_update_policy (daemon);
const char *policy_str = rpmostree_auto_update_policy_to_str (policy, NULL);
g_assert (policy_str);
rpmostree_sysroot_set_automatic_update_policy (RPMOSTREE_SYSROOT (self), policy_str);
return TRUE;
}
/* reloads *only* deployments and os internals, *no* configuration files */
static gboolean
handle_reload (RPMOSTreeSysroot *object,
GDBusMethodInvocation *invocation)
{
RpmostreedSysroot *self = RPMOSTREED_SYSROOT (object);
g_autoptr(GError) local_error = NULL;
if (!sysroot_populate_deployments_unlocked (self, NULL, &local_error))
goto out;
/* always send an UPDATED signal to also force OS interfaces to reload */
g_signal_emit (self, signals[UPDATED], 0);
rpmostree_sysroot_complete_reload (object, invocation);
out:
if (local_error)
{
g_prefix_error (&local_error, "Handling reload: ");
g_dbus_method_invocation_take_error (invocation, g_steal_pointer (&local_error));
}
return TRUE;
}
/* reloads *everything*: ostree configs, rpm-ostreed.conf, deployments, os internals */
static gboolean
handle_reload_config (RPMOSTreeSysroot *object,
GDBusMethodInvocation *invocation)
{
RpmostreedSysroot *self = RPMOSTREED_SYSROOT (object);
g_autoptr(GError) local_error = NULL;
GError **error = &local_error;
gboolean changed = FALSE;
if (!rpmostreed_daemon_reload_config (rpmostreed_daemon_get (), &changed, error))
goto out;
if (changed && !reset_config_properties (self, error))
goto out;
gboolean sysroot_changed;
if (!sysroot_reload_ostree_configs_and_deployments (self, &sysroot_changed, error))
goto out;
/* also send an UPDATED signal if configs changed to cause OS interfaces to reload; we do
* it here if not done already in `rpmostreed_sysroot_reload` */
if (changed && !sysroot_changed)
g_signal_emit (self, signals[UPDATED], 0);
rpmostree_sysroot_complete_reload_config (object, invocation);
out:
if (local_error)
{
g_prefix_error (&local_error, "Handling config reload: ");
g_dbus_method_invocation_take_error (invocation, g_steal_pointer (&local_error));
}
return TRUE;
}
/* ---------------------------------------------------------------------------------------------------- */
static void
sysroot_dispose (GObject *object)
{
RpmostreedSysroot *self = RPMOSTREED_SYSROOT (object);
gint tries;
if (self->monitor)
{
if (self->sig_changed)
g_signal_handler_disconnect (self->monitor, self->sig_changed);
self->sig_changed = 0;
/* HACK - It is not generally safe to just unref a GFileMonitor.
* Some events might be on their way to the main loop from its
* worker thread and if they arrive after the GFileMonitor has
* been destroyed, bad things will happen.
*
* As a workaround, we cancel the monitor and then spin the main
* loop a bit until nothing is pending anymore.
*
* https://bugzilla.gnome.org/show_bug.cgi?id=740491
*/
g_file_monitor_cancel (self->monitor);
for (tries = 0; tries < 10; tries ++)
{
if (!g_main_context_iteration (NULL, FALSE))
break;
}
}
/* Tracked os paths are responsible to unpublish themselves */
GLNX_HASH_TABLE_FOREACH_KV (self->os_interfaces, const char*, k, GObject*, value)
g_object_run_dispose (value);
g_hash_table_remove_all (self->os_interfaces);
g_hash_table_remove_all (self->osexperimental_interfaces);
g_clear_object (&self->transaction_monitor);
g_clear_object (&self->authority);
G_OBJECT_CLASS (rpmostreed_sysroot_parent_class)->dispose (object);
}
static void
sysroot_finalize (GObject *object)
{
RpmostreedSysroot *self = RPMOSTREED_SYSROOT (object);
_sysroot_instance = NULL;
g_hash_table_unref (self->os_interfaces);
g_hash_table_unref (self->osexperimental_interfaces);
g_clear_object (&self->monitor);
rpmostree_output_set_callback (NULL, NULL);
G_OBJECT_CLASS (rpmostreed_sysroot_parent_class)->finalize (object);
}
static void
rpmostreed_sysroot_init (RpmostreedSysroot *self)
{
g_assert (_sysroot_instance == NULL);
_sysroot_instance = self;
self->os_interfaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
(GDestroyNotify) g_object_unref);
self->osexperimental_interfaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
(GDestroyNotify) g_object_unref);
self->monitor = NULL;
self->transaction_monitor = rpmostreed_transaction_monitor_new ();
if (g_getenv ("RPMOSTREE_USE_SESSION_BUS") != NULL)
self->on_session_bus = TRUE;
/* Only use polkit when running as root on system bus; self-tests don't need it */
if (!self->on_session_bus)
{
g_autoptr(GError) local_error = NULL;
self->authority = polkit_authority_get_sync (NULL, &local_error);
if (self->authority == NULL)
{
errx (EXIT_FAILURE, "Can't get polkit authority: %s", local_error->message);
}
}
rpmostree_output_set_callback (sysroot_output_cb, self);
}
static void
sysroot_constructed (GObject *object)
{
RpmostreedSysroot *self = RPMOSTREED_SYSROOT (object);
g_object_bind_property_full (self->transaction_monitor,
"active-transaction",
self,
"active-transaction",
G_BINDING_DEFAULT |
G_BINDING_SYNC_CREATE,
sysroot_transform_transaction_to_attrs,
NULL,
NULL,
NULL);
g_object_bind_property_full (self->transaction_monitor,
"active-transaction",
self,
"active-transaction-path",
G_BINDING_DEFAULT |
G_BINDING_SYNC_CREATE,
sysroot_transform_transaction_to_address,
NULL,
NULL,
NULL);
G_OBJECT_CLASS (rpmostreed_sysroot_parent_class)->constructed (object);
}
static gboolean
sysroot_authorize_method (GDBusInterfaceSkeleton *interface,
GDBusMethodInvocation *invocation)
{
RpmostreedSysroot *self = RPMOSTREED_SYSROOT (interface);
const gchar *method_name = g_dbus_method_invocation_get_method_name (invocation);
const gchar *sender = g_dbus_method_invocation_get_sender (invocation);
gboolean authorized = FALSE;
const char *action = NULL;
if (self->on_session_bus)
{
/* The daemon is on the session bus, running self tests */
authorized = TRUE;
}
else if (g_strcmp0 (method_name, "GetOS") == 0 ||
g_strcmp0 (method_name, "Reload") == 0)
{
/* GetOS() and Reload() are always allowed */
authorized = TRUE;
}
else if (g_strcmp0 (method_name, "ReloadConfig") == 0)
{
action = "org.projectatomic.rpmostree1.reload-daemon";
}
else if (g_strcmp0 (method_name, "Cancel") == 0)
{
action = "org.projectatomic.rpmostree1.cancel";
}
else if (g_strcmp0 (method_name, "RegisterClient") == 0 ||
g_strcmp0 (method_name, "UnregisterClient") == 0)
{
action = "org.projectatomic.rpmostree1.client-management";
/* automatically allow register/unregister for users with active sessions */
uid_t uid;
if (rpmostreed_get_client_uid (rpmostreed_daemon_get (), sender, &uid))
{
g_autofree char *state = NULL;
if (sd_uid_get_state (uid, &state) >= 0)
{
if (g_strcmp0 (state, "active") == 0)
{
authorized = TRUE;
sd_journal_print (LOG_INFO, "Allowing active client %s (uid %d)",
sender, uid);
}
}
else
{
sd_journal_print (LOG_WARNING, "Failed to get state for uid %d", uid);
}
}
}
/* only ask polkit if we didn't already authorize it */
if (!authorized && action != NULL)
{
glnx_unref_object PolkitSubject *subject = polkit_system_bus_name_new (sender);
g_autoptr(GError) local_error = NULL;
glnx_unref_object PolkitAuthorizationResult *result =
polkit_authority_check_authorization_sync (self->authority, subject,
action, NULL,
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
NULL, &local_error);
if (result == NULL)
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Authorization error: %s",
local_error->message);
return FALSE;
}
authorized = polkit_authorization_result_get_is_authorized (result);
}
if (!authorized)
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"rpmostreed Sysroot operation %s not allowed for user",
method_name);
}
return authorized;
}
static void
rpmostreed_sysroot_class_init (RpmostreedSysrootClass *klass)
{
GObjectClass *gobject_class;
GDBusInterfaceSkeletonClass *gdbus_interface_skeleton_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = sysroot_dispose;
gobject_class->finalize = sysroot_finalize;
gobject_class->constructed = sysroot_constructed;
signals[UPDATED] = g_signal_new ("updated",
RPMOSTREED_TYPE_SYSROOT,
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
gdbus_interface_skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS (klass);
gdbus_interface_skeleton_class->g_authorize_method = sysroot_authorize_method;
}
static gboolean
sysroot_reload_ostree_configs_and_deployments (RpmostreedSysroot *self,
gboolean *out_changed,
GError **error)
{
gboolean ret = FALSE;
gboolean did_change;
/* reload ostree repo first so we pick up e.g. new remotes */
if (!ostree_repo_reload_config (self->repo, NULL, error))
goto out;
if (!sysroot_populate_deployments_unlocked (self, &did_change, error))
goto out;
ret = TRUE;
if (out_changed)
*out_changed = did_change;
out:
if (ret && did_change)
g_signal_emit (self, signals[UPDATED], 0);
return ret;
}
gboolean
rpmostreed_sysroot_reload (RpmostreedSysroot *self, GError **error)
{
GLNX_AUTO_PREFIX_ERROR ("Sysroot reload", error);
return sysroot_reload_ostree_configs_and_deployments (self, NULL, error);
}
static void
on_deploy_changed (GFileMonitor *monitor,
GFile *file,
GFile *other_file,
GFileMonitorEvent event_type,
gpointer user_data)
{
RpmostreedSysroot *self = RPMOSTREED_SYSROOT (user_data);
g_autoptr(GError) error = NULL;
if (event_type == G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED)
{
if (!rpmostreed_sysroot_reload (self, &error))
goto out;
}
out:
if (error)
sd_journal_print (LOG_ERR, "Unable to update state: %s", error->message);
}
static void
rpmostreed_sysroot_iface_init (RPMOSTreeSysrootIface *iface)
{
iface->handle_create_osname = handle_create_osname;
iface->handle_get_os = handle_get_os;
iface->handle_register_client = handle_register_client;
iface->handle_unregister_client = handle_unregister_client;
iface->handle_reload = handle_reload;
iface->handle_reload_config = handle_reload_config;
}
/**
* rpmostreed_sysroot_populate :
*
* loads internals and starts monitoring
*
* Returns: True on success
*/
gboolean
rpmostreed_sysroot_populate (RpmostreedSysroot *self,
GCancellable *cancellable,
GError **error)
{
g_return_val_if_fail (self != NULL, FALSE);
const char *sysroot_path = rpmostree_sysroot_get_path (RPMOSTREE_SYSROOT (self));
g_autoptr(GFile) sysroot_file = g_file_new_for_path (sysroot_path);
self->ot_sysroot = ostree_sysroot_new (sysroot_file);
/* This creates and caches an OstreeRepo instance inside
* OstreeSysroot to ensure subsequent ostree_sysroot_get_repo()
* calls won't fail.
*/
if (!ostree_sysroot_get_repo (self->ot_sysroot, &self->repo, cancellable, error))
return FALSE;
if (!sysroot_populate_deployments_unlocked (self, NULL, error))
return FALSE;
if (!reset_config_properties (self, error))
return FALSE;
if (self->monitor == NULL)
{
const char *sysroot_path = gs_file_get_path_cached (ostree_sysroot_get_path (self->ot_sysroot));
g_autofree char *sysroot_deploy_path = g_build_filename (sysroot_path, "ostree/deploy", NULL);
g_autoptr(GFile) sysroot_deploy = g_file_new_for_path (sysroot_deploy_path);
self->monitor = g_file_monitor (sysroot_deploy, 0, NULL, error);
if (self->monitor == NULL)
return FALSE;
self->sig_changed = g_signal_connect (self->monitor,
"changed",
G_CALLBACK (on_deploy_changed),
self);
}
return TRUE;
}
/* Ensures the sysroot is up to date, and returns references to the underlying
* libostree sysroot object as well as the repo. This function should
* be used at the start of both state querying and transactions.
*/
gboolean
rpmostreed_sysroot_load_state (RpmostreedSysroot *self,
GCancellable *cancellable,
OstreeSysroot **out_sysroot,
OstreeRepo **out_repo,
GError **error)
{
/* Always do a reload check here to suppress race conditions such as
* doing: ostree admin pin && rpm-ostree cleanup
* Without this we're relying on the file monitoring picking things up.
* Note that the sysroot reload checks mtimes and hence is a cheap
* no-op if nothing has changed.
*/
if (!rpmostreed_sysroot_reload (self, error))
return FALSE;
if (out_sysroot)
*out_sysroot = g_object_ref (rpmostreed_sysroot_get_root (self));
if (out_repo)
*out_repo = g_object_ref (rpmostreed_sysroot_get_repo (self));
return TRUE;
}
OstreeSysroot *
rpmostreed_sysroot_get_root (RpmostreedSysroot *self)
{
return self->ot_sysroot;
}
OstreeRepo *
rpmostreed_sysroot_get_repo (RpmostreedSysroot *self)
{
return self->repo;
}
PolkitAuthority *
rpmostreed_sysroot_get_polkit_authority (RpmostreedSysroot *self)
{
return self->authority;
}
gboolean
rpmostreed_sysroot_is_on_session_bus (RpmostreedSysroot *self)
{
return self->on_session_bus;
}
/**
* rpmostreed_sysroot_get:
*
* Returns: (transfer none): The singleton #RpmostreedSysroot instance
*/
RpmostreedSysroot *
rpmostreed_sysroot_get (void)
{
g_assert (_sysroot_instance);
return _sysroot_instance;
}