checkpoint: allow resetting the rollback timeout via D-Bus

This allows to adjust the timeout of an existing checkpoint.

The main usecase of checkpoints, is to have a fail-safe when
configuring the network remotely. By allowing to reset the timeout,
the user can perform a series of actions, and keep bumping the
timeout. That way, the entire series is still guarded by the same
checkpoint, but the user can start with short timeout, and
re-adjust the timeout as he goes along.

The libnm API only implements the async form (at least for now).
Sync methods are fundamentally wrong with D-Bus, and it's probably
not needed. Also, follow glib convenction, where the async form
doesn't have the _async name suffix. Also, accept a D-Bus path
as argument, not a NMCheckpoint instance. The libnm API should
not be more restricted than the underlying D-Bus API. It would
be cumbersome to require the user to lookup the NMCheckpoint
instance first, especially since libnm doesn't provide an efficient
or convenient lookup-by-path method. On the other hand, retrieving
the path from a NMCheckpoint instance is always possible.
This commit is contained in:
Thomas Haller 2018-03-28 08:09:56 +02:00
parent ab8312a18e
commit f67303221b
12 changed files with 303 additions and 4 deletions

View File

@ -251,6 +251,27 @@
<arg name="result" type="a{su}" direction="out" />
</method>
<!--
CheckpointAdjustRollbackTimeout:
@add_timeout: number of seconds from ~now~ in which the
timeout will expire. Set to 0 to disable the timeout.
Note that the added seconds start counting from now,
not "Created" timestamp or the previous expiration
time. Note that the "Created" property of the checkpoint
will stay unchanged by this call. However, the "RollbackTimeout"
will be recalculated to give the approximate new expiration time.
The new "RollbackTimeout" property will be approximate up to
one second precision, which is the accuracy of the property.
Reset the timeout for rollback for the checkpoint.
Since: 1.12
-->
<method name="CheckpointAdjustRollbackTimeout">
<arg name="checkpoint" type="o" direction="in"/>
<arg name="add_timeout" type="u" direction="in"/>
</method>
<!--
Devices:

View File

@ -1339,6 +1339,8 @@ global:
nm_checkpoint_get_devices;
nm_checkpoint_get_rollback_timeout;
nm_checkpoint_get_type;
nm_client_checkpoint_adjust_rollback_timeout;
nm_client_checkpoint_adjust_rollback_timeout_finish;
nm_client_checkpoint_create_async;
nm_client_checkpoint_create_finish;
nm_client_checkpoint_destroy_async;

View File

@ -2372,6 +2372,88 @@ nm_client_checkpoint_rollback_finish (NMClient *client,
}
}
static void
checkpoint_adjust_rollback_timeout_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
gs_unref_object GSimpleAsyncResult *simple = user_data;
GError *error = NULL;
if (nm_manager_checkpoint_adjust_rollback_timeout_finish (NM_MANAGER (object), result, &error))
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
else
g_simple_async_result_take_error (simple, error);
g_simple_async_result_complete (simple);
}
/**
* nm_client_checkpoint_adjust_rollback_timeout:
* @client: the %NMClient
* @checkpoint_path: a D-Bus path to a checkpoint
* @add_timeout: the timeout in seconds counting from now.
* Set to zero, to disable the timeout.
* @cancellable: a #GCancellable, or %NULL
* @callback: (scope async): callback to be called when the add operation completes
* @user_data: (closure): caller-specific data passed to @callback
*
* Resets the timeout for the checkpoint with path @checkpoint_path
* to @timeout_add.
*
* Since: 1.12
**/
void
nm_client_checkpoint_adjust_rollback_timeout (NMClient *client,
const char *checkpoint_path,
guint32 add_timeout,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *simple;
GError *error = NULL;
g_return_if_fail (NM_IS_CLIENT (client));
if (!_nm_client_check_nm_running (client, &error)) {
g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error);
return;
}
simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data,
nm_client_checkpoint_rollback_async);
if (cancellable)
g_simple_async_result_set_check_cancellable (simple, cancellable);
nm_manager_checkpoint_adjust_rollback_timeout (NM_CLIENT_GET_PRIVATE (client)->manager,
checkpoint_path, add_timeout,
cancellable, checkpoint_adjust_rollback_timeout_cb, simple);
}
/**
* nm_client_checkpoint_adjust_rollback_timeout_finish:
* @client: an #NMClient
* @result: the result passed to the #GAsyncReadyCallback
* @error: location for a #GError, or %NULL
*
* Gets the result of a call to nm_client_checkpoint_adjust_rollback_timeout().
*
* Returns: %TRUE on success or %FALSE on failure.
*
* Since: 1.12
**/
gboolean
nm_client_checkpoint_adjust_rollback_timeout_finish (NMClient *client,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (NM_IS_CLIENT (client), FALSE);
g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
error);
}
/****************************************************************/
/* Object Initialization */
/****************************************************************/

View File

@ -442,6 +442,19 @@ GHashTable *nm_client_checkpoint_rollback_finish (NMClient *client,
GAsyncResult *result,
GError **error);
NM_AVAILABLE_IN_1_12
void nm_client_checkpoint_adjust_rollback_timeout (NMClient *client,
const char *checkpoint_path,
guint32 add_timeout,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
NM_AVAILABLE_IN_1_12
gboolean nm_client_checkpoint_adjust_rollback_timeout_finish (NMClient *client,
GAsyncResult *result,
GError **error);
G_END_DECLS
#endif /* __NM_CLIENT_H__ */

View File

@ -1545,6 +1545,66 @@ nm_manager_checkpoint_rollback_finish (NMManager *manager,
return g_simple_async_result_get_op_res_gpointer (simple);
}
static void
checkpoint_adjust_rollback_timeout_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
gs_unref_object GSimpleAsyncResult *simple = user_data;
GError *error = NULL;
if (nmdbus_manager_call_checkpoint_adjust_rollback_timeout_finish (NMDBUS_MANAGER (object),
result,
&error))
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
else {
g_dbus_error_strip_remote_error (error);
g_simple_async_result_take_error (simple, error);
}
g_simple_async_result_complete (simple);
}
void
nm_manager_checkpoint_adjust_rollback_timeout (NMManager *manager,
const char *checkpoint_path,
guint32 add_timeout,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *simple;
g_return_if_fail (NM_IS_MANAGER (manager));
g_return_if_fail (checkpoint_path && checkpoint_path[0] == '/');
simple = g_simple_async_result_new (G_OBJECT (manager), callback, user_data,
nm_manager_checkpoint_adjust_rollback_timeout);
if (cancellable)
g_simple_async_result_set_check_cancellable (simple, cancellable);
nmdbus_manager_call_checkpoint_adjust_rollback_timeout (NM_MANAGER_GET_PRIVATE (manager)->proxy,
checkpoint_path,
add_timeout,
cancellable,
checkpoint_adjust_rollback_timeout_cb,
simple);
}
gboolean
nm_manager_checkpoint_adjust_rollback_timeout_finish (NMManager *manager,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *simple;
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (manager),
nm_manager_checkpoint_adjust_rollback_timeout),
FALSE);
simple = G_SIMPLE_ASYNC_RESULT (result);
return !g_simple_async_result_propagate_error (simple, error);
}
/*****************************************************************************/
static void

View File

@ -210,5 +210,14 @@ void nm_manager_checkpoint_rollback_async (NMManager *manager,
GHashTable *nm_manager_checkpoint_rollback_finish (NMManager *manager,
GAsyncResult *result,
GError **error);
void nm_manager_checkpoint_adjust_rollback_timeout (NMManager *manager,
const char *checkpoint_path,
guint32 add_timeout,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean nm_manager_checkpoint_adjust_rollback_timeout_finish (NMManager *manager,
GAsyncResult *result,
GError **error);
#endif /* __NM_MANAGER_H__ */

View File

@ -57,6 +57,7 @@ typedef struct _NMAuditManagerClass NMAuditManagerClass;
#define NM_AUDIT_OP_CHECKPOINT_CREATE "checkpoint-create"
#define NM_AUDIT_OP_CHECKPOINT_ROLLBACK "checkpoint-rollback"
#define NM_AUDIT_OP_CHECKPOINT_DESTROY "checkpoint-destroy"
#define NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT "checkpoint-adjust-rollback-timeout"
GType nm_audit_manager_get_type (void);
NMAuditManager *nm_audit_manager_get (void);

View File

@ -296,6 +296,26 @@ nm_checkpoint_manager_get_checkpoint_paths (NMCheckpointManager *self, guint *ou
return strv;
}
gboolean
nm_checkpoint_manager_adjust_rollback_timeout (NMCheckpointManager *self,
const char *path,
guint32 add_timeout,
GError **error)
{
NMCheckpoint *checkpoint;
g_return_val_if_fail (self, FALSE);
g_return_val_if_fail (path && path[0] == '/', FALSE);
g_return_val_if_fail (!error || !*error, FALSE);
checkpoint = nm_checkpoint_manager_lookup_by_path (self, path, error);
if (!checkpoint)
return FALSE;
nm_checkpoint_adjust_rollback_timeout (checkpoint, add_timeout);
return TRUE;
}
/*****************************************************************************/
NMCheckpointManager *

View File

@ -51,6 +51,11 @@ gboolean nm_checkpoint_manager_rollback (NMCheckpointManager *self,
GVariant **results,
GError **error);
gboolean nm_checkpoint_manager_adjust_rollback_timeout (NMCheckpointManager *self,
const char *path,
guint32 add_timeout,
GError **error);
const char **nm_checkpoint_manager_get_checkpoint_paths (NMCheckpointManager *self,
guint *out_length);

View File

@ -48,7 +48,7 @@ typedef struct {
NMUnmanFlagOp unmanaged_explicit;
} DeviceCheckpoint;
NM_GOBJECT_PROPERTIES_DEFINE_BASE (
NM_GOBJECT_PROPERTIES_DEFINE (NMCheckpoint,
PROP_DEVICES,
PROP_CREATED,
PROP_ROLLBACK_TIMEOUT,
@ -474,6 +474,42 @@ _timeout_cb (gpointer user_data)
return G_SOURCE_REMOVE;
}
void
nm_checkpoint_adjust_rollback_timeout (NMCheckpoint *self, guint32 add_timeout)
{
guint32 rollback_timeout_s;
gint64 now_ms, add_timeout_ms, rollback_timeout_ms;
NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
nm_clear_g_source (&priv->timeout_id);
if (add_timeout == 0)
rollback_timeout_s = 0;
else {
now_ms = nm_utils_get_monotonic_timestamp_ms ();
add_timeout_ms = ((gint64) add_timeout) * 1000;
rollback_timeout_ms = (now_ms - priv->created_at_ms) + add_timeout_ms;
/* round to nearest integer second. Since NM_CHECKPOINT_ROLLBACK_TIMEOUT is
* in units seconds, it will be able to exactly express the timeout. */
rollback_timeout_s = NM_MIN ((rollback_timeout_ms + 500) / 1000, (gint64) G_MAXUINT32);
/* we expect the timeout to be positive, because add_timeout_ms is positive.
* We cannot accept a zero, because it means "infinity". */
nm_assert (rollback_timeout_s > 0);
priv->timeout_id = g_timeout_add (NM_MIN (add_timeout_ms, (gint64) G_MAXUINT32),
_timeout_cb,
self);
}
if (rollback_timeout_s != priv->rollback_timeout_s) {
priv->rollback_timeout_s = rollback_timeout_s;
_notify (self, PROP_ROLLBACK_TIMEOUT);
}
}
/*****************************************************************************/
static void

View File

@ -61,6 +61,8 @@ void nm_checkpoint_set_timeout_callback (NMCheckpoint *self,
GVariant *nm_checkpoint_rollback (NMCheckpoint *self);
void nm_checkpoint_adjust_rollback_timeout (NMCheckpoint *self, guint32 add_timeout);
NMDevice *nm_checkpoint_includes_devices (NMCheckpoint *self, NMDevice *const*devices, guint n_devices);
NMDevice *nm_checkpoint_includes_devices_of (NMCheckpoint *self, NMCheckpoint *cp_for_devices);

View File

@ -5956,13 +5956,15 @@ checkpoint_auth_done_cb (NMAuthChain *chain,
GVariant *variant = NULL;
GError *error = NULL;
const char *arg = NULL;
guint32 add_timeout;
op = nm_auth_chain_get_data (chain, "audit-op");
priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK);
if ( nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_DESTROY)
|| nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_ROLLBACK))
if (NM_IN_STRSET (op, NM_AUDIT_OP_CHECKPOINT_DESTROY,
NM_AUDIT_OP_CHECKPOINT_ROLLBACK,
NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT))
arg = checkpoint_path = nm_auth_chain_get_data (chain, "checkpoint_path");
if (auth_error) {
@ -5995,6 +5997,10 @@ checkpoint_auth_done_cb (NMAuthChain *chain,
} else if (nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_ROLLBACK)) {
nm_checkpoint_manager_rollback (_checkpoint_mgr_get (self, TRUE),
checkpoint_path, &variant, &error);
} else if (nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT)) {
add_timeout = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "add_timeout"));
nm_checkpoint_manager_adjust_rollback_timeout (_checkpoint_mgr_get (self, TRUE),
checkpoint_path, add_timeout, &error);
} else
g_return_if_reached ();
}
@ -6007,7 +6013,6 @@ checkpoint_auth_done_cb (NMAuthChain *chain,
else
g_dbus_method_invocation_return_value (context, variant);
nm_auth_chain_unref (chain);
}
@ -6110,6 +6115,39 @@ impl_manager_checkpoint_rollback (NMDBusObject *obj,
nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE);
}
static void
impl_manager_checkpoint_adjust_rollback_timeout (NMDBusObject *obj,
const NMDBusInterfaceInfoExtended *interface_info,
const NMDBusMethodInfoExtended *method_info,
GDBusConnection *connection,
const char *sender,
GDBusMethodInvocation *invocation,
GVariant *parameters)
{
NMManager *self = NM_MANAGER (obj);
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
NMAuthChain *chain;
const char *checkpoint_path;
guint32 add_timeout;
chain = nm_auth_chain_new_context (invocation, checkpoint_auth_done_cb, self);
if (!chain) {
g_dbus_method_invocation_return_error_literal (invocation,
NM_MANAGER_ERROR,
NM_MANAGER_ERROR_PERMISSION_DENIED,
"Unable to authenticate request.");
return;
}
g_variant_get (parameters, "(&ou)", &checkpoint_path, &add_timeout);
priv->auth_chains = g_slist_append (priv->auth_chains, chain);
nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT, NULL);
nm_auth_chain_set_data (chain, "checkpoint_path", g_strdup (checkpoint_path), g_free);
nm_auth_chain_set_data (chain, "add_timeout", GUINT_TO_POINTER (add_timeout), NULL);
nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE);
}
/*****************************************************************************/
static void
@ -6964,6 +7002,16 @@ static const NMDBusInterfaceInfoExtended interface_info_manager = {
),
.handle = impl_manager_checkpoint_rollback,
),
NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
NM_DEFINE_GDBUS_METHOD_INFO_INIT (
"CheckpointAdjustRollbackTimeout",
.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
NM_DEFINE_GDBUS_ARG_INFO ("checkpoint", "o"),
NM_DEFINE_GDBUS_ARG_INFO ("add_timeout", "u"),
),
),
.handle = impl_manager_checkpoint_adjust_rollback_timeout,
),
),
.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
&nm_signal_info_property_changed_legacy,