diff options
| author | Michael Biebl <biebl@debian.org> | 2018-05-11 22:08:45 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2018-05-11 22:08:45 +0200 |
| commit | ee9c73a923909e23a649407be77e25235d769e25 (patch) | |
| tree | e21c923621fa278e737da693df9eb60ea31a6067 /src/nm-checkpoint-manager.c | |
| parent | f60117b41d5433be1b4a96d82cd11d0c3dce9b63 (diff) | |
New upstream version 1.10.8 upstream/1.10.8
Diffstat (limited to 'src/nm-checkpoint-manager.c')
| -rw-r--r-- | src/nm-checkpoint-manager.c | 326 |
1 files changed, 147 insertions, 179 deletions
diff --git a/src/nm-checkpoint-manager.c b/src/nm-checkpoint-manager.c index 8ba19db9..6da220c4 100644 --- a/src/nm-checkpoint-manager.c +++ b/src/nm-checkpoint-manager.c @@ -26,16 +26,16 @@ #include "nm-connection.h" #include "nm-core-utils.h" #include "devices/nm-device.h" +#include "nm-exported-object.h" #include "nm-manager.h" #include "nm-utils.h" -#include "c-list/src/c-list.h" /*****************************************************************************/ struct _NMCheckpointManager { NMManager *_manager; - GParamSpec *property_spec; - CList checkpoints_lst_head; + GHashTable *checkpoints; + guint rollback_timeout_id; }; #define GET_MANAGER(self) \ @@ -56,67 +56,80 @@ struct _NMCheckpointManager { /*****************************************************************************/ -static void -notify_checkpoints (NMCheckpointManager *self) { - g_object_notify_by_pspec ((GObject *) GET_MANAGER (self), - self->property_spec); -} +static void update_rollback_timeout (NMCheckpointManager *self); static void -destroy_checkpoint (NMCheckpointManager *self, NMCheckpoint *checkpoint, gboolean log_destroy) +checkpoint_destroy (gpointer checkpoint) { - nm_assert (NM_IS_CHECKPOINT (checkpoint)); - nm_assert (nm_dbus_object_is_exported (NM_DBUS_OBJECT (checkpoint))); - nm_assert (c_list_contains (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst)); - - nm_checkpoint_set_timeout_callback (checkpoint, NULL, NULL); - - c_list_unlink (&checkpoint->checkpoints_lst); - - if (log_destroy) - nm_checkpoint_log_destroy (checkpoint); - - notify_checkpoints (self); - - nm_dbus_object_unexport (NM_DBUS_OBJECT (checkpoint)); - g_object_unref (checkpoint); + nm_exported_object_unexport (NM_EXPORTED_OBJECT (checkpoint)); + g_object_unref (G_OBJECT (checkpoint)); } -static GVariant * -rollback_checkpoint (NMCheckpointManager *self, NMCheckpoint *checkpoint) +static gboolean +rollback_timeout_cb (NMCheckpointManager *self) { + NMCheckpoint *checkpoint; + GHashTableIter iter; GVariant *result; - const CList *iter; + gint64 ts, now; + + now = nm_utils_get_monotonic_timestamp_ms (); + + g_hash_table_iter_init (&iter, self->checkpoints); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &checkpoint)) { + ts = nm_checkpoint_get_rollback_ts (checkpoint); + if (ts && ts <= now) { + result = nm_checkpoint_rollback (checkpoint); + if (result) + g_variant_unref (result); + g_hash_table_iter_remove (&iter); + } + } - nm_assert (c_list_contains (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst)); + self->rollback_timeout_id = 0; + update_rollback_timeout (self); - /* we destroy first all overlapping checkpoints that are younger/newer. */ - for (iter = checkpoint->checkpoints_lst.next; - iter != &self->checkpoints_lst_head; - ) { - NMCheckpoint *cp = c_list_entry (iter, NMCheckpoint, checkpoints_lst); + return G_SOURCE_REMOVE; +} - iter = iter->next; - if (nm_checkpoint_includes_devices_of (cp, checkpoint)) { - /* the younger checkpoint has overlapping devices and gets obsoleted. - * Destroy it. */ - destroy_checkpoint (self, cp, TRUE); - } +static void +update_rollback_timeout (NMCheckpointManager *self) +{ + NMCheckpoint *checkpoint; + GHashTableIter iter; + gint64 ts, delta, next = G_MAXINT64; + + g_hash_table_iter_init (&iter, self->checkpoints); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &checkpoint)) { + ts = nm_checkpoint_get_rollback_ts (checkpoint); + if (ts && ts < next) + next = ts; } - result = nm_checkpoint_rollback (checkpoint); - destroy_checkpoint (self, checkpoint, FALSE); - return result; + nm_clear_g_source (&self->rollback_timeout_id); + + if (next != G_MAXINT64) { + delta = MAX (next - nm_utils_get_monotonic_timestamp_ms (), 0); + self->rollback_timeout_id = g_timeout_add (delta, + (GSourceFunc) rollback_timeout_cb, + self); + _LOGT ("update timeout: next check in %" G_GINT64_FORMAT " ms", delta); + } } -static void -rollback_timeout_cb (NMCheckpoint *checkpoint, - gpointer user_data) +static NMCheckpoint * +find_checkpoint_for_device (NMCheckpointManager *self, NMDevice *device) { - NMCheckpointManager *self = user_data; - gs_unref_variant GVariant *result = NULL; + GHashTableIter iter; + NMCheckpoint *checkpoint; - result = rollback_checkpoint (self, checkpoint); + g_hash_table_iter_init (&iter, self->checkpoints); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &checkpoint)) { + if (nm_checkpoint_includes_device (checkpoint, device)) + return checkpoint; + } + + return NULL; } NMCheckpoint * @@ -128,200 +141,152 @@ nm_checkpoint_manager_create (NMCheckpointManager *self, { NMManager *manager; NMCheckpoint *checkpoint; + const char * const *path; gs_unref_ptrarray GPtrArray *devices = NULL; NMDevice *device; + const char *checkpoint_path; + gs_free const char **device_paths_free = NULL; + guint i; g_return_val_if_fail (self, FALSE); g_return_val_if_fail (!error || !*error, FALSE); manager = GET_MANAGER (self); - devices = g_ptr_array_new (); - if (!device_paths || !device_paths[0]) { - const CList *tmp_lst; - - nm_manager_for_each_device (manager, device, tmp_lst) { - /* FIXME: there is no strong reason to skip over unrealized devices. - * Also, NMCheckpoint anticipates to handle them (in parts). */ + const char *device_path; + const GSList *iter; + GPtrArray *paths; + + paths = g_ptr_array_new (); + for (iter = nm_manager_get_devices (manager); + iter; + iter = g_slist_next (iter)) { + device = NM_DEVICE (iter->data); if (!nm_device_is_real (device)) continue; - nm_assert (nm_dbus_object_get_path (NM_DBUS_OBJECT (device))); - g_ptr_array_add (devices, device); + device_path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (device)); + if (device_path) + g_ptr_array_add (paths, (gpointer) device_path); } + g_ptr_array_add (paths, NULL); + device_paths_free = (const char **) g_ptr_array_free (paths, FALSE); + device_paths = (const char *const *) device_paths_free; } else if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES)) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS, "the DISCONNECT_NEW_DEVICES flag can only be used with an empty device list"); return NULL; - } else { - for (; *device_paths; device_paths++) { - device = nm_manager_get_device_by_path (manager, *device_paths); - if (!device) { - g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "device %s does not exist", *device_paths); - return NULL; - } - if (!nm_device_is_real (device)) { - g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "device %s is not realized", *device_paths); - return NULL; - } - g_ptr_array_add (devices, device); - } } - if (!devices->len) { - g_set_error_literal (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_INVALID_ARGUMENTS, - "no device available"); - return NULL; + devices = g_ptr_array_new (); + for (path = device_paths; *path; path++) { + device = nm_manager_get_device_by_path (manager, *path); + if (!device) { + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "device %s does not exist", *path); + return NULL; + } + g_ptr_array_add (devices, device); } - if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL)) - nm_checkpoint_manager_destroy_all (self); - else if (!NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING)) { - c_list_for_each_entry (checkpoint, &self->checkpoints_lst_head, checkpoints_lst) { - device = nm_checkpoint_includes_devices (checkpoint, (NMDevice *const*) devices->pdata, devices->len); - if (device) { + if (!NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL)) { + for (i = 0; i < devices->len; i++) { + device = devices->pdata[i]; + checkpoint = find_checkpoint_for_device (self, device); + if (checkpoint) { g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS, "device '%s' is already included in checkpoint %s", nm_device_get_iface (device), - nm_dbus_object_get_path (NM_DBUS_OBJECT (checkpoint))); + nm_exported_object_get_path (NM_EXPORTED_OBJECT (checkpoint))); return NULL; } } } - checkpoint = nm_checkpoint_new (manager, devices, rollback_timeout, flags); + checkpoint = nm_checkpoint_new (manager, devices, rollback_timeout, flags, error); + if (!checkpoint) + return NULL; + + if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL)) + g_hash_table_remove_all (self->checkpoints); + + nm_exported_object_export (NM_EXPORTED_OBJECT (checkpoint)); + checkpoint_path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (checkpoint)); - nm_dbus_object_export (NM_DBUS_OBJECT (checkpoint)); + if (!nm_g_hash_table_insert (self->checkpoints, + (gpointer) checkpoint_path, + checkpoint)) + g_return_val_if_reached (NULL); + + update_rollback_timeout (self); - nm_checkpoint_set_timeout_callback (checkpoint, rollback_timeout_cb, self); - c_list_link_tail (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst); - notify_checkpoints (self); return checkpoint; } -void -nm_checkpoint_manager_destroy_all (NMCheckpointManager *self) +gboolean +nm_checkpoint_manager_destroy_all (NMCheckpointManager *self, + GError **error) { - NMCheckpoint *checkpoint; + g_return_val_if_fail (self, FALSE); - g_return_if_fail (self); + g_hash_table_remove_all (self->checkpoints); - while ((checkpoint = c_list_first_entry (&self->checkpoints_lst_head, NMCheckpoint, checkpoints_lst))) - destroy_checkpoint (self, checkpoint, TRUE); + return TRUE; } gboolean nm_checkpoint_manager_destroy (NMCheckpointManager *self, - const char *path, + const char *checkpoint_path, GError **error) { - NMCheckpoint *checkpoint; + gboolean ret; g_return_val_if_fail (self, FALSE); - g_return_val_if_fail (path && path[0] == '/', FALSE); + g_return_val_if_fail (checkpoint_path && checkpoint_path[0] == '/', FALSE); g_return_val_if_fail (!error || !*error, FALSE); - if (!nm_streq (path, "/")) { - nm_checkpoint_manager_destroy_all (self); - return TRUE; - } - - checkpoint = nm_checkpoint_manager_lookup_by_path (self, path, error); - if (!checkpoint) - return FALSE; - - destroy_checkpoint (self, checkpoint, TRUE); - return TRUE; + if (!nm_streq (checkpoint_path, "/")) { + ret = g_hash_table_remove (self->checkpoints, checkpoint_path); + if (!ret) { + g_set_error (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_INVALID_ARGUMENTS, + "checkpoint %s does not exist", checkpoint_path); + } + return ret; + } else + return nm_checkpoint_manager_destroy_all (self, error); } gboolean nm_checkpoint_manager_rollback (NMCheckpointManager *self, - const char *path, + const char *checkpoint_path, GVariant **results, GError **error) { - NMCheckpoint *checkpoint; + NMCheckpoint *cp; g_return_val_if_fail (self, FALSE); - g_return_val_if_fail (path && path[0] == '/', FALSE); + g_return_val_if_fail (checkpoint_path && checkpoint_path[0] == '/', FALSE); g_return_val_if_fail (results, FALSE); g_return_val_if_fail (!error || !*error, FALSE); - checkpoint = nm_checkpoint_manager_lookup_by_path (self, path, error); - if (!checkpoint) + cp = g_hash_table_lookup (self->checkpoints, checkpoint_path); + if (!cp) { + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, + "checkpoint %s does not exist", checkpoint_path); return FALSE; - - *results = rollback_checkpoint (self, checkpoint); - return TRUE; -} - -NMCheckpoint * -nm_checkpoint_manager_lookup_by_path (NMCheckpointManager *self, const char *path, GError **error) -{ - NMCheckpoint *checkpoint; - - g_return_val_if_fail (self, NULL); - - checkpoint = (NMCheckpoint *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (GET_MANAGER (self))), - path); - if ( !checkpoint - || !NM_IS_CHECKPOINT (checkpoint)) { - g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS, - "checkpoint %s does not exist", path); - return NULL; } - nm_assert (c_list_contains (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst)); - return checkpoint; -} - -const char ** -nm_checkpoint_manager_get_checkpoint_paths (NMCheckpointManager *self, guint *out_length) -{ - NMCheckpoint *checkpoint; - const char **strv; - guint num, i = 0; + *results = nm_checkpoint_rollback (cp); + g_hash_table_remove (self->checkpoints, checkpoint_path); - num = c_list_length (&self->checkpoints_lst_head); - NM_SET_OUT (out_length, num); - if (!num) - return NULL; - - strv = g_new (const char *, num + 1); - c_list_for_each_entry (checkpoint, &self->checkpoints_lst_head, checkpoints_lst) - strv[i++] = nm_dbus_object_get_path (NM_DBUS_OBJECT (checkpoint)); - nm_assert (i == num); - strv[i] = NULL; - 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 * -nm_checkpoint_manager_new (NMManager *manager, GParamSpec *spec) +nm_checkpoint_manager_new (NMManager *manager) { NMCheckpointManager *self; @@ -336,17 +301,20 @@ nm_checkpoint_manager_new (NMManager *manager, GParamSpec *spec) * of NMManager shall surpass the lifetime of the NMCheckpointManager * instance. */ self->_manager = manager; - self->property_spec = spec; - c_list_init (&self->checkpoints_lst_head); + self->checkpoints = g_hash_table_new_full (nm_str_hash, g_str_equal, + NULL, checkpoint_destroy); + return self; } void -nm_checkpoint_manager_free (NMCheckpointManager *self) +nm_checkpoint_manager_unref (NMCheckpointManager *self) { if (!self) return; - nm_checkpoint_manager_destroy_all (self); + nm_clear_g_source (&self->rollback_timeout_id); + g_hash_table_destroy (self->checkpoints); + g_slice_free (NMCheckpointManager, self); } |