all: add version-id argument to device's Reapply method
authorThomas Haller <thaller@redhat.com>
Thu, 11 Feb 2016 08:04:58 +0000 (09:04 +0100)
committerThomas Haller <thaller@redhat.com>
Tue, 16 Feb 2016 10:24:49 +0000 (11:24 +0100)
This breaks API and ABI for the functions related to Reapply,
which got introduced in the current 1.1 development phase.

The version-id is here to allow users to error out if the connection
on the device was changed by a concurrent action.

https://bugzilla.gnome.org/show_bug.cgi?id=761714

clients/cli/devices.c
introspection/nm-device.xml
libnm-core/nm-errors.h
libnm/nm-device.c
libnm/nm-device.h
src/devices/nm-device.c

index c960c54..d2cb9fc 100644 (file)
@@ -1928,7 +1928,7 @@ do_device_reapply (NmCli *nmc, int argc, char **argv)
                info->queue = g_slist_prepend (info->queue, g_object_ref (device));
 
                /* Now reapply the connection to the device */
-               nm_device_reapply_async (device, NULL, 0, NULL, reapply_device_cb, info);
+               nm_device_reapply_async (device, NULL, 0, 0, NULL, reapply_device_cb, info);
        }
 
 error:
index 2930cf0..eada289 100644 (file)
           settings from the connection that is active on the device will be used.
         </tp:docstring>
       </arg>
+      <arg name="version_id" type="t" direction="in">
+        <tp:docstring>
+            If non-zero, the current version id of the applied connection must match.
+            This optional argument allows to catch concurrent modifications when
+            reapplying the currently applied connection with modifications.
+        </tp:docstring>
+      </arg>
       <arg name="flags" type="u" direction="in">
         <tp:docstring>
           Flags which would modify the behavior of the Reapply call.
index a985744..f5e08f9 100644 (file)
@@ -142,6 +142,7 @@ GQuark nm_crypto_error_quark (void);
  * @NM_DEVICE_ERROR_SPECIFIC_OBJECT_NOT_FOUND: the "specific object" in the
  *   activation request (eg, the #NMAccessPoint or #NMWimaxNsp) was not
  *   found.
+ * @NM_DEVICE_ERROR_VERSION_ID_MISMATCH: the version id did not match.
  *
  * Device-related errors.
  *
@@ -158,6 +159,7 @@ typedef enum {
        NM_DEVICE_ERROR_NOT_SOFTWARE,              /*< nick=NotSoftware >*/
        NM_DEVICE_ERROR_NOT_ALLOWED,               /*< nick=NotAllowed >*/
        NM_DEVICE_ERROR_SPECIFIC_OBJECT_NOT_FOUND, /*< nick=SpecificObjectNotFound >*/
+       NM_DEVICE_ERROR_VERSION_ID_MISMATCH,       /*< nick=VersionIdMismatch >*/
 } NMDeviceError;
 
 #define NM_DEVICE_ERROR nm_device_error_quark ()
index 49e1e5f..e1e77ef 100644 (file)
@@ -2167,6 +2167,9 @@ nm_device_is_software (NMDevice *device)
  * nm_device_reapply:
  * @device: a #NMDevice
  * @connection: the #NMConnection to replace the applied settings with or %NULL to reuse existing
+ * @version_id: zero or the expected version id of the applied connection. If specified
+ *   and the version id mismatches, the call fails without modification. This allows to
+ *   catch concurrent accesses.
  * @flags: always set this to zero
  * @cancellable: a #GCancellable, or %NULL
  * @error: location for a #GError, or %NULL
@@ -2181,6 +2184,7 @@ nm_device_is_software (NMDevice *device)
 gboolean
 nm_device_reapply (NMDevice *device,
                    NMConnection *connection,
+                   guint64 version_id,
                    guint flags,
                    GCancellable *cancellable,
                    GError **error)
@@ -2197,7 +2201,7 @@ nm_device_reapply (NMDevice *device,
 
 
        ret = nmdbus_device_call_reapply_sync (NM_DEVICE_GET_PRIVATE (device)->proxy,
-                                              dict, flags, cancellable, error);
+                                              dict, version_id, flags, cancellable, error);
        if (error && *error)
                g_dbus_error_strip_remote_error (*error);
        return ret;
@@ -2226,6 +2230,9 @@ device_reapply_cb (GObject *proxy,
  * nm_device_reapply_async:
  * @device: a #NMDevice
  * @connection: the #NMConnection to replace the applied settings with or %NULL to reuse existing
+ * @version_id: zero or the expected version id of the applied connection. If specified
+ *   and the version id mismatches, the call fails without modification. This allows to
+ *   catch concurrent accesses.
  * @flags: always set this to zero
  * @cancellable: a #GCancellable, or %NULL
  * @callback: callback to be called when the reapply operation completes
@@ -2239,6 +2246,7 @@ device_reapply_cb (GObject *proxy,
 void
 nm_device_reapply_async (NMDevice *device,
                          NMConnection *connection,
+                         guint64 version_id,
                          guint flags,
                          GCancellable *cancellable,
                          GAsyncReadyCallback callback,
@@ -2258,7 +2266,7 @@ nm_device_reapply_async (NMDevice *device,
                                            nm_device_reapply_async);
 
        nmdbus_device_call_reapply (NM_DEVICE_GET_PRIVATE (device)->proxy,
-                                   dict, flags, cancellable,
+                                   dict, version_id, flags, cancellable,
                                    device_reapply_cb, simple);
 }
 
index bf845ce..816acb7 100644 (file)
@@ -140,12 +140,14 @@ char **              nm_device_disambiguate_names    (NMDevice **devices,
 NM_AVAILABLE_IN_1_2
 gboolean             nm_device_reapply              (NMDevice *device,
                                                      NMConnection *connection,
+                                                     guint64 version_id,
                                                      guint flags,
                                                      GCancellable *cancellable,
                                                      GError **error);
 NM_AVAILABLE_IN_1_2
 void                 nm_device_reapply_async        (NMDevice *device,
                                                      NMConnection *connection,
+                                                     guint64 version_id,
                                                      guint flags,
                                                      GCancellable *cancellable,
                                                      GAsyncReadyCallback callback,
index c33a19d..5d9903d 100644 (file)
@@ -7162,6 +7162,8 @@ nm_device_reactivate_ip6_config (NMDevice *self,
 /* reapply_connection:
  * @connection: the new connection settings to be applied or %NULL to reapply
  *   the current settings connection
+ * @version_id: either zero, or the current version id for the applied
+ *   connection.
  * @error: the error if %FALSE is returned
  *
  * Change configuration of an already configured device if possible.
@@ -7172,6 +7174,7 @@ nm_device_reactivate_ip6_config (NMDevice *self,
 static gboolean
 reapply_connection (NMDevice *self,
                     NMConnection *connection,
+                    guint64 version_id,
                     GError **error)
 {
        NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
@@ -7181,7 +7184,6 @@ reapply_connection (NMDevice *self,
        NMConnection *con_old, *con_new;
        NMSettingIPConfig *s_ip4_old, *s_ip4_new;
        NMSettingIPConfig *s_ip6_old, *s_ip6_new;
-       guint64 version_id;
 
        if (priv->state != NM_DEVICE_STATE_ACTIVATED) {
                g_set_error_literal (error,
@@ -7213,6 +7215,15 @@ reapply_connection (NMDevice *self,
                                       NM_SETTING_CONNECTION_METERED))
                return FALSE;
 
+       if (   version_id != 0
+           && version_id != nm_active_connection_version_id_get ((NMActiveConnection *) priv->act_request)) {
+               g_set_error_literal (error,
+                                    NM_DEVICE_ERROR,
+                                    NM_DEVICE_ERROR_VERSION_ID_MISMATCH,
+                                    "Reapply failed because device changed in the meantime and the version-id mismatches");
+               return FALSE;
+       }
+
        /**************************************************************************
         * Update applied connection
         *************************************************************************/
@@ -7249,6 +7260,11 @@ reapply_connection (NMDevice *self,
        return TRUE;
 }
 
+typedef struct {
+       NMConnection *connection;
+       guint64 version_id;
+} ReapplyData;
+
 static void
 reapply_cb (NMDevice *self,
             GDBusMethodInvocation *context,
@@ -7256,9 +7272,17 @@ reapply_cb (NMDevice *self,
             GError *error,
             gpointer user_data)
 {
-       gs_unref_object NMConnection *connection = NM_CONNECTION (user_data);
+       ReapplyData *reapply_data = user_data;
+       guint64 version_id = 0;
+       gs_unref_object NMConnection *connection = NULL;
        GError *local = NULL;
 
+       if (reapply_data) {
+               connection = reapply_data->connection;
+               version_id = reapply_data->version_id;
+               g_slice_free (ReapplyData, reapply_data);
+       }
+
        if (error) {
                nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, subject, error->message);
                g_dbus_method_invocation_return_gerror (context, error);
@@ -7267,6 +7291,7 @@ reapply_cb (NMDevice *self,
 
        if (!reapply_connection (self,
                                 connection ? : (NMConnection *) nm_device_get_settings_connection (self),
+                                version_id,
                                 &local)) {
                nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, subject, local->message);
                g_dbus_method_invocation_take_error (context, local);
@@ -7281,12 +7306,14 @@ static void
 impl_device_reapply (NMDevice *self,
                      GDBusMethodInvocation *context,
                      GVariant *settings,
+                     guint64 version_id,
                      guint flags)
 {
        NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
        NMSettingsConnection *settings_connection;
        NMConnection *connection = NULL;
        GError *error = NULL;
+       ReapplyData *reapply_data;
 
        /* No flags supported as of now. */
        if (flags != 0) {
@@ -7322,6 +7349,13 @@ impl_device_reapply (NMDevice *self,
                nm_connection_clear_secrets (connection);
        }
 
+       if (connection || version_id) {
+               reapply_data = g_slice_new (ReapplyData);
+               reapply_data->connection = connection;
+               reapply_data->version_id = version_id;
+       } else
+               reapply_data = NULL;
+
        /* Ask the manager to authenticate this request for us */
        g_signal_emit (self, signals[AUTH_REQUEST], 0,
                       context,
@@ -7329,7 +7363,7 @@ impl_device_reapply (NMDevice *self,
                       NM_AUTH_PERMISSION_NETWORK_CONTROL,
                       TRUE,
                       reapply_cb,
-                      connection);
+                      reapply_data);
 }
 
 static void