diff options
Diffstat (limited to 'src/nm-checkpoint-manager.c')
| -rw-r--r-- | src/nm-checkpoint-manager.c | 326 |
1 files changed, 179 insertions, 147 deletions
diff --git a/src/nm-checkpoint-manager.c b/src/nm-checkpoint-manager.c index 6da220c4..7e8c8aea 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; - GHashTable *checkpoints; - guint rollback_timeout_id; + GParamSpec *property_spec; + CList checkpoints_lst_head; }; #define GET_MANAGER(self) \ @@ -56,80 +56,67 @@ struct _NMCheckpointManager { /*****************************************************************************/ -static void update_rollback_timeout (NMCheckpointManager *self); - static void -checkpoint_destroy (gpointer checkpoint) -{ - nm_exported_object_unexport (NM_EXPORTED_OBJECT (checkpoint)); - g_object_unref (G_OBJECT (checkpoint)); +notify_checkpoints (NMCheckpointManager *self) { + g_object_notify_by_pspec ((GObject *) GET_MANAGER (self), + self->property_spec); } -static gboolean -rollback_timeout_cb (NMCheckpointManager *self) +static void +destroy_checkpoint (NMCheckpointManager *self, NMCheckpoint *checkpoint, gboolean log_destroy) { - NMCheckpoint *checkpoint; - GHashTableIter iter; - GVariant *result; - 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 (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); - self->rollback_timeout_id = 0; - update_rollback_timeout (self); + if (log_destroy) + nm_checkpoint_log_destroy (checkpoint); - return G_SOURCE_REMOVE; + notify_checkpoints (self); + + nm_dbus_object_unexport (NM_DBUS_OBJECT (checkpoint)); + g_object_unref (checkpoint); } -static void -update_rollback_timeout (NMCheckpointManager *self) +static GVariant * +rollback_checkpoint (NMCheckpointManager *self, NMCheckpoint *checkpoint) { - 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; - } + GVariant *result; + const CList *iter; - nm_clear_g_source (&self->rollback_timeout_id); + nm_assert (c_list_contains (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst)); - 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); + /* 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); + + 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); + } } + + result = nm_checkpoint_rollback (checkpoint); + destroy_checkpoint (self, checkpoint, FALSE); + return result; } -static NMCheckpoint * -find_checkpoint_for_device (NMCheckpointManager *self, NMDevice *device) +static void +rollback_timeout_cb (NMCheckpoint *checkpoint, + gpointer user_data) { - GHashTableIter iter; - NMCheckpoint *checkpoint; + NMCheckpointManager *self = user_data; + gs_unref_variant GVariant *result = NULL; - 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; + result = rollback_checkpoint (self, checkpoint); } NMCheckpoint * @@ -141,152 +128,200 @@ 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 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); + 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). */ if (!nm_device_is_real (device)) continue; - device_path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (device)); - if (device_path) - g_ptr_array_add (paths, (gpointer) device_path); + nm_assert (nm_dbus_object_get_path (NM_DBUS_OBJECT (device))); + g_ptr_array_add (devices, device); } - 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); + } } - 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 (!devices->len) { + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_INVALID_ARGUMENTS, + "no device available"); + return NULL; } - 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) { + 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) { 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_exported_object_get_path (NM_EXPORTED_OBJECT (checkpoint))); + nm_dbus_object_get_path (NM_DBUS_OBJECT (checkpoint))); return NULL; } } } - 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)); + checkpoint = nm_checkpoint_new (manager, devices, rollback_timeout, flags); - if (!nm_g_hash_table_insert (self->checkpoints, - (gpointer) checkpoint_path, - checkpoint)) - g_return_val_if_reached (NULL); - - update_rollback_timeout (self); + nm_dbus_object_export (NM_DBUS_OBJECT (checkpoint)); + 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; } -gboolean -nm_checkpoint_manager_destroy_all (NMCheckpointManager *self, - GError **error) +void +nm_checkpoint_manager_destroy_all (NMCheckpointManager *self) { - g_return_val_if_fail (self, FALSE); + NMCheckpoint *checkpoint; - g_hash_table_remove_all (self->checkpoints); + g_return_if_fail (self); - return TRUE; + while ((checkpoint = c_list_first_entry (&self->checkpoints_lst_head, NMCheckpoint, checkpoints_lst))) + destroy_checkpoint (self, checkpoint, TRUE); } gboolean nm_checkpoint_manager_destroy (NMCheckpointManager *self, - const char *checkpoint_path, + const char *path, GError **error) { - gboolean ret; + NMCheckpoint *checkpoint; g_return_val_if_fail (self, FALSE); - g_return_val_if_fail (checkpoint_path && checkpoint_path[0] == '/', FALSE); + g_return_val_if_fail (path && path[0] == '/', FALSE); g_return_val_if_fail (!error || !*error, FALSE); - 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); + 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; } gboolean nm_checkpoint_manager_rollback (NMCheckpointManager *self, - const char *checkpoint_path, + const char *path, GVariant **results, GError **error) { - NMCheckpoint *cp; + NMCheckpoint *checkpoint; g_return_val_if_fail (self, FALSE); - g_return_val_if_fail (checkpoint_path && checkpoint_path[0] == '/', FALSE); + g_return_val_if_fail (path && path[0] == '/', FALSE); g_return_val_if_fail (results, FALSE); g_return_val_if_fail (!error || !*error, FALSE); - 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); + checkpoint = nm_checkpoint_manager_lookup_by_path (self, path, error); + if (!checkpoint) 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; } - *results = nm_checkpoint_rollback (cp); - g_hash_table_remove (self->checkpoints, checkpoint_path); + 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; + 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) +nm_checkpoint_manager_new (NMManager *manager, GParamSpec *spec) { NMCheckpointManager *self; @@ -301,20 +336,17 @@ nm_checkpoint_manager_new (NMManager *manager) * of NMManager shall surpass the lifetime of the NMCheckpointManager * instance. */ self->_manager = manager; - self->checkpoints = g_hash_table_new_full (nm_str_hash, g_str_equal, - NULL, checkpoint_destroy); - + self->property_spec = spec; + c_list_init (&self->checkpoints_lst_head); return self; } void -nm_checkpoint_manager_unref (NMCheckpointManager *self) +nm_checkpoint_manager_free (NMCheckpointManager *self) { if (!self) return; - nm_clear_g_source (&self->rollback_timeout_id); - g_hash_table_destroy (self->checkpoints); - + nm_checkpoint_manager_destroy_all (self); g_slice_free (NMCheckpointManager, self); } |