summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2018-03-28 08:09:56 +0200
committerThomas Haller <thaller@redhat.com>2018-04-04 14:02:13 +0200
commitf67303221bdc8098cb3b6203ec777b294e412971 (patch)
treec084b9d71667d8bdb8617e36cc965ee437ee751f
parentab8312a18e8b281a62f39ce59a077fda77943187 (diff)
downloadNetworkManager-f67303221bdc8098cb3b6203ec777b294e412971.tar.gz
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.
-rw-r--r--introspection/org.freedesktop.NetworkManager.xml21
-rw-r--r--libnm/libnm.ver2
-rw-r--r--libnm/nm-client.c82
-rw-r--r--libnm/nm-client.h13
-rw-r--r--libnm/nm-manager.c60
-rw-r--r--libnm/nm-manager.h9
-rw-r--r--src/nm-audit-manager.h1
-rw-r--r--src/nm-checkpoint-manager.c20
-rw-r--r--src/nm-checkpoint-manager.h5
-rw-r--r--src/nm-checkpoint.c38
-rw-r--r--src/nm-checkpoint.h2
-rw-r--r--src/nm-manager.c54
12 files changed, 303 insertions, 4 deletions
diff --git a/introspection/org.freedesktop.NetworkManager.xml b/introspection/org.freedesktop.NetworkManager.xml
index 26a618c1bf..b8ad5911f2 100644
--- a/introspection/org.freedesktop.NetworkManager.xml
+++ b/introspection/org.freedesktop.NetworkManager.xml
@@ -252,6 +252,27 @@
</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:
The list of realized network devices. Realized devices are those which
diff --git a/libnm/libnm.ver b/libnm/libnm.ver
index b99d935807..05716519d0 100644
--- a/libnm/libnm.ver
+++ b/libnm/libnm.ver
@@ -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;
diff --git a/libnm/nm-client.c b/libnm/nm-client.c
index da0c100804..9cc36d11e7 100644
--- a/libnm/nm-client.c
+++ b/libnm/nm-client.c
@@ -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 */
/****************************************************************/
diff --git a/libnm/nm-client.h b/libnm/nm-client.h
index 9833f48294..f7ec4b7a2c 100644
--- a/libnm/nm-client.h
+++ b/libnm/nm-client.h
@@ -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__ */
diff --git a/libnm/nm-manager.c b/libnm/nm-manager.c
index b8a0b9c046..57af227c23 100644
--- a/libnm/nm-manager.c
+++ b/libnm/nm-manager.c
@@ -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
diff --git a/libnm/nm-manager.h b/libnm/nm-manager.h
index ac0630ccf4..1d45bf3c2a 100644
--- a/libnm/nm-manager.h
+++ b/libnm/nm-manager.h
@@ -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__ */
diff --git a/src/nm-audit-manager.h b/src/nm-audit-manager.h
index 56e26584e5..59b8048f5a 100644
--- a/src/nm-audit-manager.h
+++ b/src/nm-audit-manager.h
@@ -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);
diff --git a/src/nm-checkpoint-manager.c b/src/nm-checkpoint-manager.c
index 8d341f56b7..59ce40dbe6 100644
--- a/src/nm-checkpoint-manager.c
+++ b/src/nm-checkpoint-manager.c
@@ -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 *
diff --git a/src/nm-checkpoint-manager.h b/src/nm-checkpoint-manager.h
index 158d670a12..ca66ef168a 100644
--- a/src/nm-checkpoint-manager.h
+++ b/src/nm-checkpoint-manager.h
@@ -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);
diff --git a/src/nm-checkpoint.c b/src/nm-checkpoint.c
index 29e748bb39..f7e857c558 100644
--- a/src/nm-checkpoint.c
+++ b/src/nm-checkpoint.c
@@ -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
diff --git a/src/nm-checkpoint.h b/src/nm-checkpoint.h
index e342650c23..c8598f380a 100644
--- a/src/nm-checkpoint.h
+++ b/src/nm-checkpoint.h
@@ -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);
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 71ccb55864..b64cb39aa4 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -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,