diff options
author | Thomas Haller <thaller@redhat.com> | 2018-03-28 08:09:56 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2018-04-04 14:02:13 +0200 |
commit | f67303221bdc8098cb3b6203ec777b294e412971 (patch) | |
tree | c084b9d71667d8bdb8617e36cc965ee437ee751f | |
parent | ab8312a18e8b281a62f39ce59a077fda77943187 (diff) | |
download | NetworkManager-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.xml | 21 | ||||
-rw-r--r-- | libnm/libnm.ver | 2 | ||||
-rw-r--r-- | libnm/nm-client.c | 82 | ||||
-rw-r--r-- | libnm/nm-client.h | 13 | ||||
-rw-r--r-- | libnm/nm-manager.c | 60 | ||||
-rw-r--r-- | libnm/nm-manager.h | 9 | ||||
-rw-r--r-- | src/nm-audit-manager.h | 1 | ||||
-rw-r--r-- | src/nm-checkpoint-manager.c | 20 | ||||
-rw-r--r-- | src/nm-checkpoint-manager.h | 5 | ||||
-rw-r--r-- | src/nm-checkpoint.c | 38 | ||||
-rw-r--r-- | src/nm-checkpoint.h | 2 | ||||
-rw-r--r-- | src/nm-manager.c | 54 |
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, |