diff options
| author | David Zeuthen <davidz@redhat.com> | 2008-04-04 23:11:52 -0400 |
|---|---|---|
| committer | David Zeuthen <davidz@redhat.com> | 2008-04-04 23:11:52 -0400 |
| commit | 9e4e5b2a1d59a8a3894632aedb595e3603239249 (patch) | |
| tree | 2247ef280365a02f8751b8cacc51b8daeeee595c /src | |
| parent | 93f11e605b8e91d1f95ec743af654ed18a743807 (diff) | |
do more work on linux-md Software RAID
Sorry for the huge patch.
Diffstat (limited to 'src')
| -rw-r--r-- | src/gdu-activatable-drive.c | 559 | ||||
| -rw-r--r-- | src/gdu-activatable-drive.h | 56 | ||||
| -rw-r--r-- | src/gdu-device.c | 165 | ||||
| -rw-r--r-- | src/gdu-device.h | 20 | ||||
| -rw-r--r-- | src/gdu-drive.c | 9 | ||||
| -rw-r--r-- | src/gdu-drive.h | 2 | ||||
| -rw-r--r-- | src/gdu-page-drive.c | 841 | ||||
| -rw-r--r-- | src/gdu-page-volume.c | 21 | ||||
| -rw-r--r-- | src/gdu-pool.c | 312 | ||||
| -rw-r--r-- | src/gdu-pool.h | 7 | ||||
| -rw-r--r-- | src/gdu-shell.c | 180 | ||||
| -rw-r--r-- | src/gdu-tree.c | 330 | ||||
| -rw-r--r-- | src/gdu-tree.h | 36 | ||||
| -rw-r--r-- | src/gdu-util.c | 40 | ||||
| -rw-r--r-- | src/gdu-util.h | 1 | ||||
| -rw-r--r-- | src/gdu-volume.c | 9 | ||||
| -rw-r--r-- | src/gdu-volume.h | 2 |
17 files changed, 2180 insertions, 410 deletions
diff --git a/src/gdu-activatable-drive.c b/src/gdu-activatable-drive.c index d4522ee..79bb6ee 100644 --- a/src/gdu-activatable-drive.c +++ b/src/gdu-activatable-drive.c @@ -37,7 +37,6 @@ struct _GduActivatableDrivePrivate GduActivableDriveKind kind; GduPool *pool; - char *uuid; GList *slaves; }; @@ -56,10 +55,15 @@ static void device_changed (GduDevice *device, gpointer user_data); static void gdu_activatable_drive_finalize (GduActivatableDrive *activatable_drive) { + GList *l; + if (activatable_drive->priv->device != NULL) { - g_signal_handlers_disconnect_by_func (activatable_drive->priv->device, device_changed, activatable_drive); - g_signal_handlers_disconnect_by_func (activatable_drive->priv->device, device_job_changed, activatable_drive); - g_signal_handlers_disconnect_by_func (activatable_drive->priv->device, device_removed, activatable_drive); + g_signal_handlers_disconnect_by_func (activatable_drive->priv->device, device_changed, + activatable_drive); + g_signal_handlers_disconnect_by_func (activatable_drive->priv->device, device_job_changed, + activatable_drive); + g_signal_handlers_disconnect_by_func (activatable_drive->priv->device, device_removed, + activatable_drive); g_object_unref (activatable_drive->priv->device); } @@ -67,8 +71,12 @@ gdu_activatable_drive_finalize (GduActivatableDrive *activatable_drive) g_object_unref (activatable_drive->priv->pool); } - g_free (activatable_drive->priv->uuid); - g_list_foreach (activatable_drive->priv->slaves, (GFunc) g_object_unref, NULL); + for (l = activatable_drive->priv->slaves; l != NULL; l = l->next) { + GduDevice *device = GDU_DEVICE (l->data); + g_signal_handlers_disconnect_by_func (device, device_changed, activatable_drive); + g_signal_handlers_disconnect_by_func (device, device_job_changed, activatable_drive); + g_object_unref (device); + } g_list_free (activatable_drive->priv->slaves); if (G_OBJECT_CLASS (parent_class)->finalize) @@ -97,6 +105,7 @@ device_changed (GduDevice *device, gpointer user_data) { GduActivatableDrive *activatable_drive = GDU_ACTIVATABLE_DRIVE (user_data); g_signal_emit_by_name (activatable_drive, "changed"); + g_signal_emit_by_name (activatable_drive->priv->pool, "presentable-changed", activatable_drive); } static void @@ -104,6 +113,7 @@ device_job_changed (GduDevice *device, gpointer user_data) { GduActivatableDrive *activatable_drive = GDU_ACTIVATABLE_DRIVE (user_data); g_signal_emit_by_name (activatable_drive, "job-changed"); + g_signal_emit_by_name (activatable_drive->priv->pool, "presentable-job-changed", activatable_drive); } static void @@ -115,19 +125,63 @@ device_removed (GduDevice *device, gpointer user_data) GduActivatableDrive * gdu_activatable_drive_new (GduPool *pool, - GduActivableDriveKind kind, - const char *uuid) + GduActivableDriveKind kind) { GduActivatableDrive *activatable_drive; activatable_drive = GDU_ACTIVATABLE_DRIVE (g_object_new (GDU_TYPE_ACTIVATABLE_DRIVE, NULL)); activatable_drive->priv->pool = g_object_ref (pool); activatable_drive->priv->kind = kind; - activatable_drive->priv->uuid = g_strdup (uuid); return activatable_drive; } +gboolean +gdu_activatable_drive_has_uuid (GduActivatableDrive *activatable_drive, + const char *uuid) +{ + gboolean ret; + GduDevice *first_slave; + + ret = FALSE; + + if (activatable_drive->priv->slaves == NULL) + goto out; + + first_slave = GDU_DEVICE (activatable_drive->priv->slaves->data); + if (strcmp (gdu_device_linux_md_component_get_uuid (first_slave), uuid) == 0) + ret = TRUE; + +out: + return ret; +} + +gboolean +gdu_activatable_drive_device_references_slave (GduActivatableDrive *activatable_drive, + GduDevice *device) +{ + int n; + gboolean ret; + char **slaves; + + ret = FALSE; + + if (activatable_drive->priv->device == NULL) + goto out; + + slaves = gdu_device_linux_md_get_slaves (activatable_drive->priv->device); + for (n = 0; slaves[n] != NULL; n++) { + if (strcmp (slaves[n], gdu_device_get_object_path (device)) == 0) { + ret = TRUE; + goto out; + } + } + +out: + return ret; +} + + GduActivableDriveKind gdu_activatable_drive_get_kind (GduActivatableDrive *activatable_drive) { @@ -156,7 +210,13 @@ gdu_activatable_drive_get_name (GduPresentable *presentable) GduActivatableDrive *activatable_drive = GDU_ACTIVATABLE_DRIVE (presentable); GduDevice *device; char *ret; - int level; + char *level_str; + char *size_str; + guint64 component_size; + guint64 raid_size; + int num_slaves; + int num_raid_devices; + const char *level; const char *name; ret = NULL; @@ -174,12 +234,50 @@ gdu_activatable_drive_get_name (GduPresentable *presentable) case GDU_ACTIVATABLE_DRIVE_KIND_LINUX_MD: level = gdu_device_linux_md_component_get_level (device); name = gdu_device_linux_md_component_get_name (device); + num_raid_devices = gdu_device_linux_md_component_get_num_raid_devices (device); + num_slaves = g_list_length (activatable_drive->priv->slaves); + component_size = gdu_device_get_size (device); + + raid_size = gdu_presentable_get_size (GDU_PRESENTABLE (activatable_drive)); + + if (strcmp (level, "raid0") == 0) { + level_str = g_strdup (_("RAID-0")); + } else if (strcmp (level, "raid1") == 0) { + level_str = g_strdup (_("RAID-1")); + } else if (strcmp (level, "raid4") == 0) { + level_str = g_strdup (_("RAID-4")); + } else if (strcmp (level, "raid5") == 0) { + level_str = g_strdup (_("RAID-5")); + } else if (strcmp (level, "raid6") == 0) { + level_str = g_strdup (_("RAID-6")); + } else if (strcmp (level, "raid10") == 0) { + level_str = g_strdup (_("RAID-10")); + } else if (strcmp (level, "linear") == 0) { + level_str = g_strdup (_("Linear")); + } else { + level_str = g_strdup (level); + } + + if (raid_size == 0) + size_str = NULL; + else + size_str = gdu_util_get_size_for_display (raid_size, FALSE); if (name == NULL || strlen (name) == 0) { - ret = g_strdup_printf (_("RAID-%d drive"), level); + if (size_str != NULL) + ret = g_strdup_printf (_("%s %s Drive"), size_str, level_str); + else + ret = g_strdup_printf (_("%s Drive"), level_str); } else { - ret = g_strdup_printf (_("RAID-%d %s"), level, name); + if (size_str != NULL) + ret = g_strdup_printf (_("%s %s (%s)"), size_str, name, level_str); + else + ret = g_strdup_printf (_("%s (%s)"), name, level_str); } + + g_free (level_str); + g_free (size_str); + break; } @@ -201,7 +299,8 @@ gdu_activatable_drive_get_icon_name (GduPresentable *presentable) /* explicit fallthrough */ case GDU_ACTIVATABLE_DRIVE_KIND_LINUX_MD: - ret = g_strdup ("drive-removable-media-floppy"); + /* TODO: get this into the naming spec */ + ret = g_strdup ("gdu-raid-array"); break; } return ret; @@ -217,11 +316,86 @@ static guint64 gdu_activatable_drive_get_size (GduPresentable *presentable) { GduActivatableDrive *activatable_drive = GDU_ACTIVATABLE_DRIVE (presentable); + GduDevice *device; + guint64 ret; + const char *level; + int num_raid_devices; + int n; + guint component_size; + GList *l; + + ret = 0; + if (activatable_drive->priv->device != NULL) { - return gdu_device_get_size (activatable_drive->priv->device); + ret = gdu_device_get_size (activatable_drive->priv->device); } else { - return 0; + if (activatable_drive->priv->slaves == NULL) { + g_warning ("%s: no device and no slaves", __FUNCTION__); + goto out; + } + + device = GDU_DEVICE (activatable_drive->priv->slaves->data); + + level = gdu_device_linux_md_component_get_level (device); + num_raid_devices = gdu_device_linux_md_component_get_num_raid_devices (device); + component_size = gdu_device_get_size (device); + + if (strcmp (level, "raid0") == 0) { + /* stripes in linux can have different sizes */ + + if (g_list_length (activatable_drive->priv->slaves) == num_raid_devices) { + n = 0; + for (l = activatable_drive->priv->slaves; l != NULL; l = l->next) { + GduDevice *sd = GDU_DEVICE (l->data); + GduActivableDriveSlaveState slave_state; + + slave_state = gdu_activatable_drive_get_slave_state (activatable_drive, sd); + if (slave_state == GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_READY) { + ret += gdu_device_get_size (sd);; + n++; + } + } + if (n != num_raid_devices) { + ret = 0; + } + } + + } else if (strcmp (level, "raid1") == 0) { + ret = component_size; + } else if (strcmp (level, "raid4") == 0) { + ret = component_size * (num_raid_devices - 1) / num_raid_devices; + } else if (strcmp (level, "raid5") == 0) { + ret = component_size * (num_raid_devices - 1) / num_raid_devices; + } else if (strcmp (level, "raid6") == 0) { + ret = component_size * (num_raid_devices - 2) / num_raid_devices; + } else if (strcmp (level, "raid10") == 0) { + /* TODO: need to figure out out to compute this */ + } else if (strcmp (level, "linear") == 0) { + + if (g_list_length (activatable_drive->priv->slaves) == num_raid_devices) { + n = 0; + for (l = activatable_drive->priv->slaves; l != NULL; l = l->next) { + GduDevice *sd = GDU_DEVICE (l->data); + GduActivableDriveSlaveState slave_state; + + slave_state = gdu_activatable_drive_get_slave_state (activatable_drive, sd); + if (slave_state == GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_READY) { + ret += gdu_device_get_size (sd);; + n++; + } + } + if (n != num_raid_devices) { + ret = 0; + } + } + + } else { + g_warning ("%s: unknown level '%s'", __FUNCTION__, level); + } } + +out: + return ret; } static GList * @@ -349,6 +523,12 @@ gdu_activatable_drive_is_recognized (GduPresentable *presentable) return TRUE; } +gboolean +gdu_activatable_drive_is_device_set (GduActivatableDrive *activatable_drive) +{ + return activatable_drive->priv->device != NULL; +} + void gdu_activatable_drive_set_device (GduActivatableDrive *activatable_drive, GduDevice *device) { @@ -373,12 +553,29 @@ gdu_activatable_drive_set_device (GduActivatableDrive *activatable_drive, GduDev g_signal_emit_by_name (activatable_drive, "changed"); } +gboolean +gdu_activatable_drive_has_slave (GduActivatableDrive *activatable_drive, + GduDevice *device) +{ + return g_list_find (activatable_drive->priv->slaves, device) != NULL; +} + void gdu_activatable_drive_add_slave (GduActivatableDrive *activatable_drive, GduDevice *device) { - activatable_drive->priv->slaves = g_list_prepend (activatable_drive->priv->slaves, device); + activatable_drive->priv->slaves = g_list_append (activatable_drive->priv->slaves, device); g_signal_emit_by_name (activatable_drive, "changed"); + g_signal_emit_by_name (activatable_drive->priv->pool, "presentable-changed", activatable_drive); + + /* We're also interested in 'changed' events from the slave; forward them onto + * ourselves. + * + * There's no need to watch for removed; the owner (GduPool typically) is responsible for + * removing slaves when they disappear. + */ + g_signal_connect (device, "changed", (GCallback) device_changed, activatable_drive); + g_signal_connect (device, "job-changed", (GCallback) device_job_changed, activatable_drive); } void @@ -387,6 +584,10 @@ gdu_activatable_drive_remove_slave (GduActivatableDrive *activatable_drive, { activatable_drive->priv->slaves = g_list_remove (activatable_drive->priv->slaves, device); g_signal_emit_by_name (activatable_drive, "changed"); + g_signal_emit_by_name (activatable_drive->priv->pool, "presentable-changed", activatable_drive); + + g_signal_handlers_disconnect_by_func (device, device_changed, activatable_drive); + g_signal_handlers_disconnect_by_func (device, device_job_changed, activatable_drive); } GList * @@ -413,6 +614,26 @@ gdu_activatable_drive_get_num_slaves (GduActivatableDrive *activatable_drive) return g_list_length (activatable_drive->priv->slaves); } +int +gdu_activatable_drive_get_num_ready_slaves (GduActivatableDrive *activatable_drive) +{ + GList *l; + GduDevice *slave; + int num_ready_slaves; + GduActivableDriveSlaveState slave_state; + + num_ready_slaves = 0; + for (l = activatable_drive->priv->slaves; l != NULL; l = l->next) { + slave = GDU_DEVICE (l->data); + slave_state = gdu_activatable_drive_get_slave_state (activatable_drive, slave); + if (slave_state == GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_READY) { + num_ready_slaves++; + } + } + + return num_ready_slaves; +} + static void gdu_activatable_drive_presentable_iface_init (GduPresentableIface *iface) { @@ -427,3 +648,309 @@ gdu_activatable_drive_presentable_iface_init (GduPresentableIface *iface) iface->is_allocated = gdu_activatable_drive_is_allocated; iface->is_recognized = gdu_activatable_drive_is_recognized; } + +gboolean +gdu_activatable_drive_is_activated (GduActivatableDrive *activatable_drive) +{ + return activatable_drive->priv->device != NULL; +} + +/** + * gdu_activatable_drive_can_activate: + * @activatable_drive: A #GduActivatableDrive. + * + * Check if @activatable_drive can be activated in non-degraded + * mode. See also gdu_activatable_drive_can_activate_degraded(). + * + * Returns: #TRUE only if it can be activated. If @activatable_drive + * is already activated this function returns #FALSE. + **/ +gboolean +gdu_activatable_drive_can_activate (GduActivatableDrive *activatable_drive) +{ + int num_slaves; + int num_ready_slaves; + int num_raid_devices; + GduDevice *slave; + GduActivableDriveSlaveState slave_state; + gboolean can_activate; + GList *l; + + can_activate = FALSE; + + /* can't activated what's already activated */ + if (activatable_drive->priv->device != NULL) + goto out; + + switch (activatable_drive->priv->kind) { + case GDU_ACTIVATABLE_DRIVE_KIND_LINUX_MD: + + num_raid_devices = -1; + num_slaves = 0; + num_ready_slaves = 0; + + /* count the number of slaves in the READY state */ + for (l = activatable_drive->priv->slaves; l != NULL; l = l->next) { + slave = GDU_DEVICE (l->data); + + num_slaves++; + + slave_state = gdu_activatable_drive_get_slave_state (activatable_drive, slave); + if (slave_state == GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_READY) { + num_raid_devices = gdu_device_linux_md_component_get_num_raid_devices (slave); + num_ready_slaves++; + } + } + + /* we can activate only if all slaves are in the READY */ + if (num_ready_slaves == num_raid_devices) { + can_activate = TRUE; + } + break; + + default: + g_warning ("unknown kind %d", activatable_drive->priv->kind); + break; + } +out: + return can_activate; +} + +/** + * gdu_activatable_drive_can_activate_degraded: + * @activatable_drive: A #GduActivatableDrive. + * + * Check if @activatable_drive can be activated in degraded mode. See + * also gdu_activatable_drive_can_activate(). + * + * Returns: #TRUE only if it can be activated in degraded mode. If + * @activatable_drive is already activated or can be activated in + * non-degraded mode, this function returns #FALSE. + **/ +gboolean +gdu_activatable_drive_can_activate_degraded (GduActivatableDrive *activatable_drive) +{ + GduDevice *device; + gboolean can_activate_degraded; + + can_activate_degraded = FALSE; + + /* can't activated what's already activated */ + if (activatable_drive->priv->device != NULL) + goto out; + + /* we might even be able to activate in non-degraded mode */ + if (gdu_activatable_drive_can_activate (activatable_drive)) + goto out; + + switch (activatable_drive->priv->kind) { + case GDU_ACTIVATABLE_DRIVE_KIND_LINUX_MD: + device = gdu_activatable_drive_get_first_slave (activatable_drive); + if (device != NULL) { + int num_ready_slaves; + int num_raid_devices; + const char *raid_level; + + num_ready_slaves = gdu_activatable_drive_get_num_ready_slaves (activatable_drive); + num_raid_devices = gdu_device_linux_md_component_get_num_raid_devices (device); + raid_level = gdu_device_linux_md_component_get_level (device); + + /* this depends on the raid level... */ + if (strcmp (raid_level, "raid1") == 0) { + if (num_ready_slaves >= 1) { + can_activate_degraded = TRUE; + } + } else if (strcmp (raid_level, "raid4") == 0) { + if (num_ready_slaves >= num_raid_devices - 1) { + can_activate_degraded = TRUE; + } + } else if (strcmp (raid_level, "raid5") == 0) { + if (num_ready_slaves >= num_raid_devices - 1) { + can_activate_degraded = TRUE; + } + } else if (strcmp (raid_level, "raid6") == 0) { + if (num_ready_slaves >= num_raid_devices - 2) { + can_activate_degraded = TRUE; + } + } else if (strcmp (raid_level, "raid10") == 0) { + /* TODO: This is not necessarily correct; it depends on which + * slaves have failed... Right now we err on the side + * of saying the array can be activated even when sometimes + * it can't + */ + if (num_ready_slaves >= num_raid_devices / 2) { + can_activate_degraded = TRUE; + } + } + g_object_unref (device); + } + break; + + default: + g_warning ("unknown kind %d", activatable_drive->priv->kind); + break; + } + +out: + return can_activate_degraded; +} + +typedef struct +{ + GduActivatableDrive *activatable_drive; + GduActivatableDriveActivationFunc callback; + gpointer user_data; +} ActivationData; + +static ActivationData * +activation_data_new (GduActivatableDrive *activatable_drive, + GduActivatableDriveActivationFunc callback, + gpointer user_data) +{ + ActivationData *ad; + ad = g_new0 (ActivationData, 1); + ad->activatable_drive = g_object_ref (activatable_drive); + ad->callback = callback; + ad->user_data = user_data; + return ad; +} + +static void +activation_data_free (ActivationData *ad) +{ + g_object_unref (ad->activatable_drive); + g_free (ad); +} + +static void +activation_completed (GduPool *pool, + const char *assembled_array_object_path, + GError *error, + gpointer user_data) +{ + ActivationData *ad = user_data; + ad->callback (ad->activatable_drive, assembled_array_object_path != NULL, error, ad->user_data); + activation_data_free (ad); +} + +void +gdu_activatable_drive_activate (GduActivatableDrive *activatable_drive, + GduActivatableDriveActivationFunc callback, + gpointer user_data) +{ + GPtrArray *components; + GList *l; + GList *slaves; + + g_return_if_fail (activatable_drive->priv->kind == GDU_ACTIVATABLE_DRIVE_KIND_LINUX_MD); + g_return_if_fail (activatable_drive->priv->device == NULL); + + components = g_ptr_array_new (); + slaves = gdu_activatable_drive_get_slaves (activatable_drive); + for (l = slaves; l != NULL; l = l->next) { + GduDevice *d = l->data; + /* no need to dup; we keep a ref on d for the lifetime of components */ + g_ptr_array_add (components, (gpointer) gdu_device_get_object_path (d)); + } + + gdu_pool_op_assemble_linux_md_array (activatable_drive->priv->pool, + components, + activation_completed, + activation_data_new (activatable_drive, callback, user_data)); + + g_ptr_array_free (components, TRUE); + g_list_foreach (slaves, (GFunc) g_object_unref, NULL); + g_list_free (slaves); +} + +void +gdu_activatable_drive_deactivate (GduActivatableDrive *activatable_drive) +{ + g_return_if_fail (activatable_drive->priv->kind == GDU_ACTIVATABLE_DRIVE_KIND_LINUX_MD); + g_return_if_fail (activatable_drive->priv->device != NULL); + + gdu_device_op_stop_linux_md_array (activatable_drive->priv->device); +} + +GduActivableDriveSlaveState +gdu_activatable_drive_get_slave_state (GduActivatableDrive *activatable_drive, + GduDevice *slave) +{ + GList *l; + guint64 max_event_number; + gboolean one_of_us; + GduActivableDriveSlaveState ret; + + g_return_val_if_fail (activatable_drive->priv->kind == GDU_ACTIVATABLE_DRIVE_KIND_LINUX_MD, -1); + + ret = -1; + + /* array is running */ + if (activatable_drive->priv->device != NULL) { + int n; + char **array_slaves; + char **slaves_state; + + array_slaves = gdu_device_linux_md_get_slaves (activatable_drive->priv->device); + slaves_state = gdu_device_linux_md_get_slaves_state (activatable_drive->priv->device); + for (n = 0; array_slaves[n] != NULL; n++) { + if (strcmp (gdu_device_get_object_path (slave), array_slaves[n]) == 0) { + const char *state = slaves_state[n]; + + if (strcmp (state, "in_sync") == 0) { + ret = GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_RUNNING; + } else if (strcmp (state, "sync_in_progress") == 0) { + ret = GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_RUNNING_SYNCING; + } else if (strcmp (state, "spare") == 0) { + ret = GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_RUNNING_HOT_SPARE; + } else { + g_warning ("unknown state '%s' for '%s", state, array_slaves[n]); + ret = GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_RUNNING; + } + goto out; + } + } + + ret = GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_NOT_FRESH; + goto out; + } + + /* array is not running */ + + one_of_us = FALSE; + + /* first find the biggest event number */ + max_event_number = 0; + for (l = activatable_drive->priv->slaves; l != NULL; l = l->next) { + GduDevice *d = GDU_DEVICE (l->data); + guint64 event_number; + + if (!gdu_device_is_linux_md_component (d)) { + g_warning ("slave is not linux md component!"); + break; + } + + event_number = gdu_device_linux_md_component_get_events (d); + if (event_number > max_event_number) + max_event_number = event_number; + + if (d == slave) + one_of_us = TRUE; + } + + if (!one_of_us) { + g_warning ("given device is not a slave of the activatable drive"); + goto out; + } + + /* if our event number equals the max then it's all good */ + if (max_event_number == gdu_device_linux_md_component_get_events (slave)) { + ret = GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_READY; + } else { + /* otherwise we're stale */ + ret = GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_NOT_FRESH; + } + +out: + return ret; +} diff --git a/src/gdu-activatable-drive.h b/src/gdu-activatable-drive.h index e2b98ec..d4630e8 100644 --- a/src/gdu-activatable-drive.h +++ b/src/gdu-activatable-drive.h @@ -56,20 +56,72 @@ typedef enum { GDU_ACTIVATABLE_DRIVE_KIND_LINUX_MD, } GduActivableDriveKind; +/** + * GduActivableDriveSlaveState: + * @GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_RUNNING: The drive is activated and the + * slave is part of it. + * @GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_RUNNING_SYNCING: The drive is activated + * and the slave is part of the array but is in the process of being synced + * into the array (e.g. it was recently added). + * @GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_RUNNING_HOT_SPARE: The drive is activated and + the slave is a hot spare ready to kick in if a drive fails. + * @GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_READY: The drive is not activated and the + * slave, compared to other slaves, is a valid member of the array. + * @GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_NOT_FRESH: Either the drive is not + * activated but the slave, compared to other slaves, is not valid since + * other slaves have higher event numbers / more recent usage dates / etc. + * Otherwise, if the drive is activated, slaves with this type have valid + * meta-data (e.g. uuid) but is not part of the array (typically happens + * if the wire connecting the slave was temporarily disconnected). + * + * State for slaves of an activatable drive. + **/ +typedef enum { + GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_RUNNING, + GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_RUNNING_SYNCING, + GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_RUNNING_HOT_SPARE, + GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_READY, + GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_NOT_FRESH, +} GduActivableDriveSlaveState; + GType gdu_activatable_drive_get_type (void); GduActivatableDrive *gdu_activatable_drive_new (GduPool *pool, - GduActivableDriveKind kind, - const char *uuid); + GduActivableDriveKind kind); void gdu_activatable_drive_set_device (GduActivatableDrive *activatable_drive, GduDevice *device); +gboolean gdu_activatable_drive_is_device_set (GduActivatableDrive *activatable_drive); void gdu_activatable_drive_add_slave (GduActivatableDrive *activatable_drive, GduDevice *device); void gdu_activatable_drive_remove_slave (GduActivatableDrive *activatable_drive, GduDevice *device); +gboolean gdu_activatable_drive_has_uuid (GduActivatableDrive *activatable_drive, + const char *uuid); +gboolean gdu_activatable_drive_device_references_slave (GduActivatableDrive *activatable_drive, + GduDevice *device); + +gboolean gdu_activatable_drive_has_slave (GduActivatableDrive *activatable_drive, + GduDevice *device); GList *gdu_activatable_drive_get_slaves (GduActivatableDrive *activatable_drive); GduDevice *gdu_activatable_drive_get_first_slave (GduActivatableDrive *activatable_drive); int gdu_activatable_drive_get_num_slaves (GduActivatableDrive *activatable_drive); +int gdu_activatable_drive_get_num_ready_slaves (GduActivatableDrive *activatable_drive); GduActivableDriveKind gdu_activatable_drive_get_kind (GduActivatableDrive *activatable_drive); +typedef void (*GduActivatableDriveActivationFunc) (GduActivatableDrive *activatable_drive, + gboolean success, + GError *error, + gpointer user_data); + + +gboolean gdu_activatable_drive_is_activated (GduActivatableDrive *activatable_drive); +gboolean gdu_activatable_drive_can_activate (GduActivatableDrive *activatable_drive); +gboolean gdu_activatable_drive_can_activate_degraded (GduActivatableDrive *activatable_drive); +void gdu_activatable_drive_activate (GduActivatableDrive *activatable_drive, + GduActivatableDriveActivationFunc callback, + gpointer user_data); +void gdu_activatable_drive_deactivate (GduActivatableDrive *activatable_drive); + +GduActivableDriveSlaveState gdu_activatable_drive_get_slave_state (GduActivatableDrive *activatable_drive, + GduDevice *slave); #endif /* GDU_ACTIVATABLE_DRIVE_H */ diff --git a/src/gdu-device.c b/src/gdu-device.c index 795e584..fbeed67 100644 --- a/src/gdu-device.c +++ b/src/gdu-device.c @@ -100,18 +100,23 @@ typedef struct char **drive_media_compatibility; char *drive_media; - int linux_md_component_level; + char *linux_md_component_level; int linux_md_component_num_raid_devices; char *linux_md_component_uuid; char *linux_md_component_name; char *linux_md_component_version; + guint64 linux_md_component_update_time; + guint64 linux_md_component_events; - int linux_md_level; + char *linux_md_level; int linux_md_num_raid_devices; - char *linux_md_uuid; - char *linux_md_name; char *linux_md_version; char **linux_md_slaves; + char **linux_md_slaves_state; + gboolean linux_md_is_degraded; + char *linux_md_sync_action; + double linux_md_sync_percentage; + guint64 linux_md_sync_speed; } DeviceProperties; static void @@ -241,7 +246,7 @@ collect_props (const char *key, const GValue *value, DeviceProperties *props) props->drive_media = g_strdup (g_value_get_string (value)); else if (strcmp (key, "linux-md-component-level") == 0) - props->linux_md_component_level = g_value_get_int (value); + props->linux_md_component_level = g_strdup (g_value_get_string (value)); else if (strcmp (key, "linux-md-component-num-raid-devices") == 0) props->linux_md_component_num_raid_devices = g_value_get_int (value); else if (strcmp (key, "linux-md-component-uuid") == 0) @@ -250,15 +255,15 @@ collect_props (const char *key, const GValue *value, DeviceProperties *props) props->linux_md_component_name = g_strdup (g_value_get_string (value)); else if (strcmp (key, "linux-md-component-version") == 0) props->linux_md_component_version = g_strdup (g_value_get_string (value)); + else if (strcmp (key, "linux-md-component-update-time") == 0) + props->linux_md_component_update_time = g_value_get_uint64 (value); + else if (strcmp (key, "linux-md-component-events") == 0) + props->linux_md_component_events = g_value_get_uint64 (value); else if (strcmp (key, "linux-md-level") == 0) - props->linux_md_level = g_value_get_int (value); + props->linux_md_level = g_strdup (g_value_get_string (value)); else if (strcmp (key, "linux-md-num-raid-devices") == 0) props->linux_md_num_raid_devices = g_value_get_int (value); - else if (strcmp (key, "linux-md-uuid") == 0) - props->linux_md_uuid = g_strdup (g_value_get_string (value)); - else if (strcmp (key, "linux-md-name") == 0) - props->linux_md_name = g_strdup (g_value_get_string (value)); else if (strcmp (key, "linux-md-version") == 0) props->linux_md_version = g_strdup (g_value_get_string (value)); else if (strcmp (key, "linux-md-slaves") == 0) { @@ -272,6 +277,16 @@ collect_props (const char *key, const GValue *value, DeviceProperties *props) props->linux_md_slaves[n] = g_strdup (object_paths->pdata[n]); props->linux_md_slaves[n] = NULL; } + else if (strcmp (key, "linux-md-slaves-state") == 0) + props->linux_md_slaves_state = g_strdupv (g_value_get_boxed (value)); + else if (strcmp (key, "linux-md-is-degraded") == 0) + props->linux_md_is_degraded = g_value_get_boolean (value); + else if (strcmp (key, "linux-md-sync-action") == 0) + props->linux_md_sync_action = g_strdup (g_value_get_string (value)); + else if (strcmp (key, "linux-md-sync-percentage") == 0) + props->linux_md_sync_percentage = g_value_get_double (value); + else if (strcmp (key, "linux-md-sync-speed") == 0) + props->linux_md_sync_speed = g_value_get_uint64 (value); else handled = FALSE; @@ -351,13 +366,15 @@ device_properties_free (DeviceProperties *props) g_free (props->drive_connection_interface); g_strfreev (props->drive_media_compatibility); g_free (props->drive_media); + g_free (props->linux_md_component_level); g_free (props->linux_md_component_uuid); g_free (props->linux_md_component_name); g_free (props->linux_md_component_version); - g_free (props->linux_md_uuid); - g_free (props->linux_md_name); + g_free (props->linux_md_level); g_free (props->linux_md_version); g_strfreev (props->linux_md_slaves); + g_strfreev (props->linux_md_slaves_state); + g_free (props->linux_md_sync_action); g_free (props); } @@ -847,7 +864,7 @@ gdu_device_drive_get_media (GduDevice *device) return device->priv->props->drive_media; } -int +const char * gdu_device_linux_md_component_get_level (GduDevice *device) { return device->priv->props->linux_md_component_level; @@ -877,28 +894,28 @@ gdu_device_linux_md_component_get_version (GduDevice *device) return device->priv->props->linux_md_component_version; } -int -gdu_device_linux_md_get_level (GduDevice *device) +guint64 +gdu_device_linux_md_component_get_update_time (GduDevice *device) { - return device->priv->props->linux_md_level; + return device->priv->props->linux_md_component_update_time; } -int -gdu_device_linux_md_get_num_raid_devices (GduDevice *device) +guint64 +gdu_device_linux_md_component_get_events (GduDevice *device) { - return device->priv->props->linux_md_num_raid_devices; + return device->priv->props->linux_md_component_events; } const char * -gdu_device_linux_md_get_uuid (GduDevice *device) +gdu_device_linux_md_get_level (GduDevice *device) { - return device->priv->props->linux_md_uuid; + return device->priv->props->linux_md_level; } -const char * -gdu_device_linux_md_get_name (GduDevice *device) +int +gdu_device_linux_md_get_num_raid_devices (GduDevice *device) { - return device->priv->props->linux_md_name; + return device->priv->props->linux_md_num_raid_devices; } const char * @@ -913,6 +930,38 @@ gdu_device_linux_md_get_slaves (GduDevice *device) return device->priv->props->linux_md_slaves; } +char ** +gdu_device_linux_md_get_slaves_state (GduDevice *device) +{ + return device->priv->props->linux_md_slaves_state; +} + +gboolean +gdu_device_linux_md_is_degraded (GduDevice *device) +{ + return device->priv->props->linux_md_is_degraded; +} + +const char * +gdu_device_linux_md_get_sync_action (GduDevice *device) +{ + return device->priv->props->linux_md_sync_action; +} + +double +gdu_device_linux_md_get_sync_percentage (GduDevice *device) +{ + return device->priv->props->linux_md_sync_percentage; +} + +guint64 +gdu_device_linux_md_get_sync_speed (GduDevice *device) +{ + return device->priv->props->linux_md_sync_speed; +} + +/* ---------------------------------------------------------------------------------------------------- */ + gboolean gdu_device_job_in_progress (GduDevice *device) { @@ -1580,6 +1629,74 @@ gdu_device_op_stop_linux_md_array (GduDevice *device) /* -------------------------------------------------------------------------------- */ +static void +op_add_component_to_linux_md_array_cb (DBusGProxy *proxy, GError *error, gpointer user_data) +{ + GduDevice *device = GDU_DEVICE (user_data); + if (error != NULL) { + g_warning ("op_add_component_to_linux_md_array_cb failed: %s", error->message); + gdu_device_job_set_failed (device, error); + g_error_free (error); + } + g_object_unref (device); +} + +void +gdu_device_op_add_component_to_linux_md_array (GduDevice *device, const char *component_objpath) +{ + char *options[16]; + + options[0] = NULL; + + org_freedesktop_DeviceKit_Disks_Device_add_component_to_linux_md_array_async ( + device->priv->proxy, + component_objpath, + (const char **) options, + op_add_component_to_linux_md_array_cb, + g_object_ref (device)); +} + +/* -------------------------------------------------------------------------------- */ + +static void +op_remove_component_from_linux_md_array_cb (DBusGProxy *proxy, GError *error, gpointer user_data) +{ + GduDevice *device = GDU_DEVICE (user_data); + if (error != NULL) { + g_warning ("op_remove_component_from_linux_md_array_cb failed: %s", error->message); + gdu_device_job_set_failed (device, error); + g_error_free (error); + } + g_object_unref (device); +} + +void +gdu_device_op_remove_component_from_linux_md_array (GduDevice *device, + const char *component_objpath, + const char *secure_erase) +{ + int n; + char *options[16]; + + n = 0; + if (secure_erase != NULL && strlen (secure_erase) > 0) { + options[n++] = g_strdup_printf ("erase=%s", secure_erase); + } + options[n] = NULL; + + org_freedesktop_DeviceKit_Disks_Device_remove_component_from_linux_md_array_async ( + device->priv->proxy, + component_objpath, + (const char **) options, + op_remove_component_from_linux_md_array_cb, + g_object_ref (device)); + + while (n >= 0) + g_free (options[n--]); +} + +/* -------------------------------------------------------------------------------- */ + void gdu_device_op_cancel_job (GduDevice *device) { diff --git a/src/gdu-device.h b/src/gdu-device.h index da9c216..4cf0ccc 100644 --- a/src/gdu-device.h +++ b/src/gdu-device.h @@ -133,18 +133,23 @@ guint64 gdu_device_drive_get_connection_speed (GduDevice *device); char **gdu_device_drive_get_media_compatibility (GduDevice *device); const char *gdu_device_drive_get_media (GduDevice *device); -int gdu_device_linux_md_component_get_level (GduDevice *device); +const char *gdu_device_linux_md_component_get_level (GduDevice *device); int gdu_device_linux_md_component_get_num_raid_devices (GduDevice *device); const char *gdu_device_linux_md_component_get_uuid (GduDevice *device); const char *gdu_device_linux_md_component_get_name (GduDevice *device); const char *gdu_device_linux_md_component_get_version (GduDevice *device); +guint64 gdu_device_linux_md_component_get_update_time (GduDevice *device); +guint64 gdu_device_linux_md_component_get_events (GduDevice *device); -int gdu_device_linux_md_get_level (GduDevice *device); +const char *gdu_device_linux_md_get_level (GduDevice *device); int gdu_device_linux_md_get_num_raid_devices (GduDevice *device); -const char *gdu_device_linux_md_get_uuid (GduDevice *device); -const char *gdu_device_linux_md_get_name (GduDevice *device); const char *gdu_device_linux_md_get_version (GduDevice *device); char **gdu_device_linux_md_get_slaves (GduDevice *device); +char **gdu_device_linux_md_get_slaves_state (GduDevice *device); +gboolean gdu_device_linux_md_is_degraded (GduDevice *device); +const char *gdu_device_linux_md_get_sync_action (GduDevice *device); +double gdu_device_linux_md_get_sync_percentage (GduDevice *device); +guint64 gdu_device_linux_md_get_sync_speed (GduDevice *device); /* ---------------------------------------------------------------------------------------------------- */ /* fire and forget ops */ @@ -171,6 +176,13 @@ void gdu_device_op_run_smart_selftest (GduDevice *device, void gdu_device_op_stop_linux_md_array (GduDevice *device); +void gdu_device_op_add_component_to_linux_md_array (GduDevice *device, + const char *component_objpath); + +void gdu_device_op_remove_component_from_linux_md_array (GduDevice *device, + const char *component_objpath, + const char *secure_erase); + /* ---------------------------------------------------------------------------------------------------- */ /* ops where feedback is essential */ /* ---------------------------------------------------------------------------------------------------- */ diff --git a/src/gdu-drive.c b/src/gdu-drive.c index 8a7bc94..b2c4314 100644 --- a/src/gdu-drive.c +++ b/src/gdu-drive.c @@ -32,6 +32,7 @@ struct _GduDrivePrivate { GduDevice *device; + GduPool *pool; }; static GObjectClass *parent_class = NULL; @@ -55,6 +56,9 @@ gdu_drive_finalize (GduDrive *drive) g_object_unref (drive->priv->device); } + if (drive->priv->pool != NULL) + g_object_unref (drive->priv->pool); + if (G_OBJECT_CLASS (parent_class)->finalize) (* G_OBJECT_CLASS (parent_class)->finalize) (G_OBJECT (drive)); } @@ -81,6 +85,7 @@ device_changed (GduDevice *device, gpointer user_data) { GduDrive *drive = GDU_DRIVE (user_data); g_signal_emit_by_name (drive, "changed"); + g_signal_emit_by_name (drive->priv->pool, "presentable-changed", drive); } static void @@ -88,6 +93,7 @@ device_job_changed (GduDevice *device, gpointer user_data) { GduDrive *drive = GDU_DRIVE (user_data); g_signal_emit_by_name (drive, "job-changed"); + g_signal_emit_by_name (drive->priv->pool, "presentable-job-changed", drive); } static void @@ -98,12 +104,13 @@ device_removed (GduDevice *device, gpointer user_data) } GduDrive * -gdu_drive_new_from_device (GduDevice *device) +gdu_drive_new_from_device (GduPool *pool, GduDevice *device) { GduDrive *drive; drive = GDU_DRIVE (g_object_new (GDU_TYPE_DRIVE, NULL)); drive->priv->device = g_object_ref (device); + drive->priv->pool = g_object_ref (pool); g_signal_connect (device, "changed", (GCallback) device_changed, drive); g_signal_connect (device, "job-changed", (GCallback) device_job_changed, drive); g_signal_connect (device, "removed", (GCallback) device_removed, drive); diff --git a/src/gdu-drive.h b/src/gdu-drive.h index c0d07d1..fa488a8 100644 --- a/src/gdu-drive.h +++ b/src/gdu-drive.h @@ -51,6 +51,6 @@ struct _GduDriveClass }; GType gdu_drive_get_type (void); -GduDrive *gdu_drive_new_from_device (GduDevice *drive); +GduDrive *gdu_drive_new_from_device (GduPool *pool, GduDevice *drive); #endif /* GDU_DRIVE_H */ diff --git a/src/gdu-page-drive.c b/src/gdu-page-drive.c index 185ed12..f7567c3 100644 --- a/src/gdu-page-drive.c +++ b/src/gdu-page-drive.c @@ -28,6 +28,7 @@ #include "gdu-page.h" #include "gdu-page-drive.h" #include "gdu-util.h" +#include "gdu-tree.h" #include "gdu-drive.h" #include "gdu-activatable-drive.h" @@ -51,12 +52,30 @@ struct _GduPageDrivePrivate GtkWidget *create_part_table_vbox; GtkWidget *create_part_table_type_combo_box; - GtkWidget *linux_md_explanatory_label; + GtkWidget *linux_md_name_label; + GtkWidget *linux_md_type_label; + GtkWidget *linux_md_size_label; + GtkWidget *linux_md_components_label; + GtkWidget *linux_md_state_label; + GtkWidget *linux_md_tree_view; + GtkTreeStore *linux_md_tree_store; + GtkWidget *linux_md_add_to_array_button; + GtkWidget *linux_md_remove_from_array_button; + GtkWidget *linux_md_add_new_to_array_button; PolKitAction *pk_create_part_table_action; PolKitGnomeAction *create_part_table_action; }; +enum { + MD_LINUX_ICON_COLUMN, + MD_LINUX_NAME_COLUMN, + MD_LINUX_STATE_STRING_COLUMN, + MD_LINUX_STATE_COLUMN, + MD_LINUX_OBJPATH_COLUMN, + MD_LINUX_N_COLUMNS, +}; + static GObjectClass *parent_class = NULL; static void gdu_page_drive_page_iface_init (GduPageIface *iface); @@ -71,6 +90,11 @@ enum { static void health_refresh_button_clicked (GtkWidget *button, gpointer user_data); static void health_selftest_button_clicked (GtkWidget *button, gpointer user_data); +static void add_to_array_button_clicked (GtkWidget *button, gpointer user_data); +static void add_new_to_array_button_clicked (GtkWidget *button, gpointer user_data); +static void remove_from_array_button_clicked (GtkWidget *button, gpointer user_data); + +static void linux_md_tree_changed (GtkTreeSelection *selection, gpointer user_data); static void gdu_page_drive_finalize (GduPageDrive *page) @@ -231,6 +255,9 @@ gdu_page_drive_init (GduPageDrive *page) GtkWidget *button; GtkWidget *button_box; GtkWidget *image; + GtkWidget *scrolled_window; + GtkWidget *tree_view; + GtkTreeSelection *selection; page->priv = g_new0 (GduPageDrivePrivate, 1); @@ -481,22 +508,156 @@ gdu_page_drive_init (GduPageDrive *page) vbox3 = gtk_vbox_new (FALSE, 5); gtk_notebook_append_page (GTK_NOTEBOOK (page->priv->notebook), vbox3, NULL); + table = gtk_table_new (4, 2, FALSE); + gtk_box_pack_start (GTK_BOX (vbox3), table, FALSE, FALSE, 0); + + row = 0; + + /* name */ + label = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); + gtk_label_set_markup (GTK_LABEL (label), _("<b>Array Name:</b>")); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2); label = gtk_label_new (NULL); - gtk_label_set_markup (GTK_LABEL (label), _("<b>RAID Drive</b>")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); - gtk_box_pack_start (GTK_BOX (vbox3), label, FALSE, FALSE, 0); - vbox2 = gtk_vbox_new (FALSE, 5); - align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0); - gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 24, 0); - gtk_container_add (GTK_CONTAINER (align), vbox2); - gtk_box_pack_start (GTK_BOX (vbox3), align, FALSE, TRUE, 0); + gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2); + page->priv->linux_md_name_label = label; - /* explanatory text */ + row++; + + /* size */ + label = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); + gtk_label_set_markup (GTK_LABEL (label), _("<b>Array Size:</b>")); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2); label = gtk_label_new (NULL); - gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); - gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, TRUE, 0); - page->priv->linux_md_explanatory_label = label; + gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2); + page->priv->linux_md_size_label = label; + + row++; + + /* type (level) */ + label = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); + gtk_label_set_markup (GTK_LABEL (label), _("<b>RAID Type:</b>")); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2); + label = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2); + page->priv->linux_md_type_label = label; + + row++; + + /* components */ + label = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); + gtk_label_set_markup (GTK_LABEL (label), _("<b>Components:</b>")); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2); + label = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2); + page->priv->linux_md_components_label = label; + + row++; + + /* components */ + label = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); + gtk_label_set_markup (GTK_LABEL (label), _("<b>State:</b>")); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2); + label = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1, + GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 2); + page->priv->linux_md_state_label = label; + + row++; + + tree_view = gtk_tree_view_new (); + page->priv->linux_md_tree_view = tree_view; + scrolled_window = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view); + gtk_box_pack_start (GTK_BOX (vbox3), scrolled_window, TRUE, TRUE, 0); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + g_signal_connect (selection, "changed", (GCallback) linux_md_tree_changed, page); + + button_box = gtk_hbutton_box_new (); + gtk_button_box_set_layout (GTK_BUTTON_BOX (button_box), GTK_BUTTONBOX_START); + gtk_box_set_spacing (GTK_BOX (button_box), 6); + gtk_box_set_homogeneous (GTK_BOX (button_box), FALSE); + gtk_box_pack_start (GTK_BOX (vbox3), button_box, FALSE, FALSE, 0); + + button = gtk_button_new_with_mnemonic (_("A_ttach")); + gtk_button_set_image (GTK_BUTTON (button), + gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON)); + gtk_container_add (GTK_CONTAINER (button_box), button); + page->priv->linux_md_add_to_array_button = button; + g_signal_connect (button, "clicked", G_CALLBACK (add_to_array_button_clicked), page); + gtk_widget_set_tooltip_text (button, _("Attaches the stale component to the RAID array. " + "After attachment, data from the array will be " + "synchronized on the component.")); + + button = gtk_button_new_with_mnemonic (_("_Detach")); + gtk_button_set_image (GTK_BUTTON (button), + gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_BUTTON)); + gtk_container_add (GTK_CONTAINER (button_box), button); + page->priv->linux_md_remove_from_array_button = button; + g_signal_connect (button, "clicked", G_CALLBACK (remove_from_array_button_clicked), page); + gtk_widget_set_tooltip_text (button, _("Detaches the running component from the RAID array. Data on " + "the component will be erased and the volume will be ready " + "for other use.")); + + button = gtk_button_new_with_mnemonic (_("_Add...")); + gtk_button_set_image (GTK_BUTTON (button), + gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_BUTTON)); + gtk_container_add (GTK_CONTAINER (button_box), button); + page->priv->linux_md_add_new_to_array_button = button; + g_signal_connect (button, "clicked", G_CALLBACK (add_new_to_array_button_clicked), page); + gtk_widget_set_tooltip_text (button, _("Adds a new component to the running RAID array. Use this " + "when replacing a failed component or adding a hot spare.")); + + /* add renderers for tree view */ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_title (column, _("RAID Component")); + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, + "pixbuf", MD_LINUX_ICON_COLUMN, + NULL); + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, renderer, TRUE); + gtk_tree_view_column_set_attributes (column, renderer, + "text", MD_LINUX_NAME_COLUMN, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column); + + column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_title (column, _("State")); + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, + "text", MD_LINUX_STATE_STRING_COLUMN, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column); + + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), TRUE); } @@ -725,23 +886,467 @@ out: /* ---------------------------------------------------------------------------------------------------- */ static void +add_new_to_array_button_clicked (GtkWidget *button, gpointer user_data) +{ + GduPageDrive *page = GDU_PAGE_DRIVE (user_data); + GduPresentable *presentable; + GduPresentable *selected_presentable; + GduDevice *device; + GduDevice *selected_device; + GduActivatableDrive *activatable_drive; + GduPool *pool; + GtkWidget *dialog; + GtkWidget *vbox; + int response; + GtkWidget *tree_view; + char *array_name; + char *s; + + device = NULL; + selected_device = NULL; + pool = NULL; + array_name = NULL; + selected_presentable = NULL; + + presentable = gdu_shell_get_selected_presentable (page->priv->shell); + if (!GDU_IS_ACTIVATABLE_DRIVE (presentable)) { + g_warning ("%s: is not an activatable drive", __FUNCTION__); + goto out; + } + + device = gdu_presentable_get_device (presentable); + if (device == NULL) { + g_warning ("%s: activatable drive not active", __FUNCTION__); + goto out; + } + + activatable_drive = GDU_ACTIVATABLE_DRIVE (presentable); + + pool = gdu_device_get_pool (device); + + dialog = gtk_dialog_new_with_buttons ("", + GTK_WINDOW (gdu_shell_get_toplevel (page->priv->shell)), + GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT|GTK_DIALOG_NO_SEPARATOR, + NULL); + + + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2); + gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area), 5); + gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->action_area), 6); + + GtkWidget *hbox; + + hbox = gtk_hbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, TRUE, TRUE, 0); + + GtkWidget *image; + + image = gtk_image_new_from_pixbuf (gdu_util_get_pixbuf_for_presentable (presentable, GTK_ICON_SIZE_DIALOG)); + gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); + gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + + vbox = gtk_vbox_new (FALSE, 10); + gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); + + array_name = gdu_presentable_get_name (presentable); + + GtkWidget *label; + label = gtk_label_new (NULL); + s = g_strdup_printf ( _("<big><b>Select a volume to use as component in the array \"%s\"</b></big>\n\n" + "Only volumes of acceptable sizes can be selected. You may " + "need to manually create new volumes of acceptable sizes."), + array_name); + gtk_label_set_markup (GTK_LABEL (label), s); + g_free (s); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 0); + + + GtkWidget *scrolled_window; + scrolled_window = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), + GTK_SHADOW_IN); + tree_view = gdu_device_tree_new (pool); + gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view); + + gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0); + + gtk_widget_grab_focus (gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL)); + gtk_dialog_add_button (GTK_DIALOG (dialog), _("Add _Volume"), 0); + + gtk_window_set_default_size (GTK_WINDOW (dialog), -1, 350); + + gtk_widget_show_all (dialog); + response = gtk_dialog_run (GTK_DIALOG (dialog)); + + selected_presentable = gdu_device_tree_get_selected_presentable (GTK_TREE_VIEW (tree_view)); + if (selected_presentable != NULL) + g_object_ref (selected_presentable); + gtk_widget_destroy (dialog); + + if (response < 0) + goto out; + + if (selected_presentable == NULL) + goto out; + + selected_device = gdu_presentable_get_device (selected_presentable); + if (selected_device == NULL) + goto out; + + g_warning ("got it: %s", gdu_device_get_object_path (selected_device)); + + /* got it! */ + gdu_device_op_add_component_to_linux_md_array (device, gdu_device_get_object_path (selected_device)); + + +out: + g_free (array_name); + if (selected_presentable != NULL) + g_object_unref (selected_presentable); + if (selected_device != NULL) + g_object_unref (selected_device); + if (device != NULL) + g_object_unref (device); + if (pool != NULL) + g_object_unref (pool); +} + +static void +add_to_array_button_clicked (GtkWidget *button, gpointer user_data) +{ + GtkTreePath *path; + GduPageDrive *page = GDU_PAGE_DRIVE (user_data); + GduDevice *device; + GduPresentable *presentable; + GduActivatableDrive *activatable_drive; + GduDevice *slave_device; + GduPool *pool; + GduActivableDriveSlaveState slave_state; + char *component_objpath; + + device = NULL; + slave_device = NULL; + pool = NULL; + component_objpath = NULL; + + presentable = gdu_shell_get_selected_presentable (page->priv->shell); + if (!GDU_IS_ACTIVATABLE_DRIVE (presentable)) { + g_warning ("%s: is not an activatable drive", __FUNCTION__); + goto out; + } + + device = gdu_presentable_get_device (presentable); + if (device == NULL) { + g_warning ("%s: activatable drive not active", __FUNCTION__); + goto out; + } + + activatable_drive = GDU_ACTIVATABLE_DRIVE (presentable); + + gtk_tree_view_get_cursor (GTK_TREE_VIEW (page->priv->linux_md_tree_view), &path, NULL); + if (path != NULL) { + GtkTreeIter iter; + + if (gtk_tree_model_get_iter (GTK_TREE_MODEL (page->priv->linux_md_tree_store), &iter, path)) { + + gtk_tree_model_get (GTK_TREE_MODEL (page->priv->linux_md_tree_store), &iter, + MD_LINUX_OBJPATH_COLUMN, + &component_objpath, + -1); + } + gtk_tree_path_free (path); + } + + if (component_objpath == NULL) { + g_warning ("%s: no component selected", __FUNCTION__); + goto out; + } + + pool = gdu_device_get_pool (device); + + slave_device = gdu_pool_get_by_object_path (pool, component_objpath); + if (slave_device == NULL) { + g_warning ("%s: no device for component objpath %s", __FUNCTION__, component_objpath); + goto out; + } + + slave_state = gdu_activatable_drive_get_slave_state (activatable_drive, slave_device); + if (slave_state == GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_NOT_FRESH) { + /* yay, add this to the array */ + gdu_device_op_add_component_to_linux_md_array (device, component_objpath); + } + + +out: + g_free (component_objpath); + if (device != NULL) + g_object_unref (device); + if (pool != NULL) + g_object_unref (pool); + if (slave_device != NULL) + g_object_unref (slave_device); +} + +static void +remove_from_array_button_clicked (GtkWidget *button, gpointer user_data) +{ + GtkTreePath *path; + GduPageDrive *page = GDU_PAGE_DRIVE (user_data); + GduDevice *device; + GduPresentable *presentable; + GduActivatableDrive *activatable_drive; + GduDevice *slave_device; + GduPool *pool; + GduActivableDriveSlaveState slave_state; + char *component_objpath; + GduPresentable *slave_presentable; + + device = NULL; + slave_device = NULL; + pool = NULL; + component_objpath = NULL; + slave_presentable = NULL; + + presentable = gdu_shell_get_selected_presentable (page->priv->shell); + if (!GDU_IS_ACTIVATABLE_DRIVE (presentable)) { + g_warning ("%s: is not an activatable drive", __FUNCTION__); + goto out; + } + + device = gdu_presentable_get_device (presentable); + if (device == NULL) { + g_warning ("%s: activatable drive not active", __FUNCTION__); + goto out; + } + + activatable_drive = GDU_ACTIVATABLE_DRIVE (presentable); + + gtk_tree_view_get_cursor (GTK_TREE_VIEW (page->priv->linux_md_tree_view), &path, NULL); + if (path != NULL) { + GtkTreeIter iter; + + if (gtk_tree_model_get_iter (GTK_TREE_MODEL (page->priv->linux_md_tree_store), &iter, path)) { + + gtk_tree_model_get (GTK_TREE_MODEL (page->priv->linux_md_tree_store), &iter, + MD_LINUX_OBJPATH_COLUMN, + &component_objpath, + -1); + } + gtk_tree_path_free (path); + } + + if (component_objpath == NULL) { + g_warning ("%s: no component selected", __FUNCTION__); + goto out; + } + + pool = gdu_device_get_pool (device); + + slave_device = gdu_pool_get_by_object_path (pool, component_objpath); + if (slave_device == NULL) { + g_warning ("%s: no device for component objpath %s", __FUNCTION__, component_objpath); + goto out; + } + + slave_presentable = gdu_pool_get_volume_by_device (pool, slave_device); + if (slave_presentable == NULL) { + g_warning ("%s: no volume for component objpath %s", __FUNCTION__, component_objpath); + goto out; + } + + slave_state = gdu_activatable_drive_get_slave_state (activatable_drive, slave_device); + if (slave_state == GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_RUNNING || + slave_state == GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_RUNNING_SYNCING || + slave_state == GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_RUNNING_HOT_SPARE) { + char *primary; + char *secondary; + char *secure_erase; + char *array_name; + char *component_name; + + array_name = gdu_presentable_get_name (presentable); + component_name = gdu_presentable_get_name (slave_presentable); + + /* confirmation dialog */ + primary = g_strdup (_("<b><big>Are you sure you want to remove the component from the array?</big></b>")); + + secondary = g_strdup_printf (_("The data on component \"%s\" of the RAID Array \"%s\" will be " + "irrecovably erased and the RAID Array might be degraded. " + "Make sure important data is backed up. " + "This action cannot be undone."), + component_name, + array_name); + + secure_erase = gdu_util_delete_confirmation_dialog (gdu_shell_get_toplevel (page->priv->shell), + "", + primary, + secondary, + _("_Remove Component")); + if (secure_erase != NULL) { + /* yay, remove this component from the array */ + gdu_device_op_remove_component_from_linux_md_array (device, component_objpath, secure_erase); + } + + g_free (primary); + g_free (secondary); + g_free (secure_erase); + g_free (array_name); + g_free (component_name); + } + + +out: + g_free (component_objpath); + if (device != NULL) + g_object_unref (device); + if (pool != NULL) + g_object_unref (pool); + if (slave_device != NULL) + g_object_unref (slave_device); + if (slave_presentable != NULL) + g_object_unref (slave_presentable); +} + +static void +linux_md_buttons_update (GduPageDrive *page) +{ + GtkTreePath *path; + char *component_objpath; + gboolean show_add_to_array_button; + gboolean show_add_new_to_array_button; + gboolean show_remove_from_array_button; + GduPresentable *presentable; + GduActivatableDrive *activatable_drive; + GduDevice *device; + GduDevice *slave_device; + GduPool *pool; + GduActivableDriveSlaveState slave_state; + + component_objpath = NULL; + device = NULL; + slave_device = NULL; + pool = NULL; + show_add_to_array_button = FALSE; + show_add_new_to_array_button = FALSE; + show_remove_from_array_button = FALSE; + + gtk_tree_view_get_cursor (GTK_TREE_VIEW (page->priv->linux_md_tree_view), &path, NULL); + if (path != NULL) { + GtkTreeIter iter; + + if (gtk_tree_model_get_iter (GTK_TREE_MODEL (page->priv->linux_md_tree_store), &iter, path)) { + + gtk_tree_model_get (GTK_TREE_MODEL (page->priv->linux_md_tree_store), &iter, + MD_LINUX_OBJPATH_COLUMN, + &component_objpath, + -1); + } + gtk_tree_path_free (path); + } + + presentable = gdu_shell_get_selected_presentable (page->priv->shell); + if (!GDU_IS_ACTIVATABLE_DRIVE (presentable)) { + g_warning ("%s: is not an activatable drive", __FUNCTION__); + goto out; + } + + activatable_drive = GDU_ACTIVATABLE_DRIVE (presentable); + + /* can only add/remove components on a running drive */ + device = gdu_presentable_get_device (presentable); + if (device == NULL) { + goto out; + } + + /* can always add a new component when the drive is running */ + show_add_new_to_array_button = TRUE; + + pool = gdu_device_get_pool (device); + + if (component_objpath != NULL) { + + slave_device = gdu_pool_get_by_object_path (pool, component_objpath); + if (slave_device == NULL) { + g_warning ("%s: no device for component objpath %s", __FUNCTION__, component_objpath); + goto out; + } + + slave_state = gdu_activatable_drive_get_slave_state (activatable_drive, slave_device); + + if (slave_state == GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_NOT_FRESH) { + /* yay, we can add this to the array */ + show_add_to_array_button = TRUE; + } + + if (slave_state == GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_RUNNING || + slave_state == GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_RUNNING_SYNCING || + slave_state == GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_RUNNING_HOT_SPARE) { + /* yay, we can remove this to the array */ + show_remove_from_array_button = TRUE; + } + } + +out: + gtk_widget_set_sensitive (page->priv->linux_md_add_to_array_button, show_add_to_array_button); + gtk_widget_set_sensitive (page->priv->linux_md_add_new_to_array_button, show_add_new_to_array_button); + gtk_widget_set_sensitive (page->priv->linux_md_remove_from_array_button, show_remove_from_array_button); + + g_free (component_objpath); + if (device != NULL) + g_object_unref (device); + if (pool != NULL) + g_object_unref (pool); + if (slave_device != NULL) + g_object_unref (slave_device); +} + +static void +linux_md_tree_changed (GtkTreeSelection *selection, gpointer user_data) +{ + GduPageDrive *page = GDU_PAGE_DRIVE (user_data); + linux_md_buttons_update (page); +} + +static const GtkTargetEntry dnd_targets[1] = { + {"STRING", 0, 0}, +}; + +static const int num_dnd_targets = 1; + +static void linux_md_section_update (GduPageDrive *page, gboolean reset_page) { char *s; GduDevice *device; GduPresentable *presentable; GduActivatableDrive *activatable_drive; + GList *l; GList *slaves; GduDevice *component; const char *uuid; const char *name; - int level; + const char *level; int num_raid_devices; int num_slaves; + char *level_str; + guint64 component_size; + guint64 raid_size; + char *raid_size_str; + char *components_str; + char *state_str; - s = NULL; slaves = NULL; device = NULL; + level_str = NULL; + raid_size_str = NULL; + components_str = NULL; + state_str = NULL; presentable = gdu_shell_get_selected_presentable (page->priv->shell); activatable_drive = GDU_ACTIVATABLE_DRIVE (presentable); @@ -765,23 +1370,213 @@ linux_md_section_update (GduPageDrive *page, gboolean reset_page) uuid = gdu_device_linux_md_component_get_uuid (component); name = gdu_device_linux_md_component_get_name (component); + if (name == NULL || strlen (name) == 0) { + name = _("-"); + } level = gdu_device_linux_md_component_get_level (component); num_raid_devices = gdu_device_linux_md_component_get_num_raid_devices (component); + component_size = gdu_device_get_size (component); + + /*g_warning ("activatable drive:\n" + "uuid=%s\n" + "name=%s\n" + "level=%s\n" + "num_raid_devices=%d\n" + "num_slaves=%d\n" + "device=%p", + uuid, name, level, num_raid_devices, num_slaves, device);*/ + + raid_size = gdu_presentable_get_size (presentable); + + if (strcmp (level, "raid0") == 0) { + level_str = g_strdup (_("Striped (RAID-0)")); + } else if (strcmp (level, "raid1") == 0) { + level_str = g_strdup (_("Mirrored (RAID-1)")); + } else if (strcmp (level, "raid4") == 0) { + level_str = g_strdup (_("RAID-4")); + } else if (strcmp (level, "raid5") == 0) { + level_str = g_strdup (_("RAID-5")); + } else if (strcmp (level, "raid6") == 0) { + level_str = g_strdup (_("RAID-6")); + } else if (strcmp (level, "linear") == 0) { + level_str = g_strdup (_("Linear (Just a Bunch Of Disks)")); + } else { + level_str = g_strdup (level); + } + + s = gdu_util_get_size_for_display (component_size, FALSE); + if (strcmp (level, "linear") == 0) { + components_str = g_strdup_printf (_("%d Components"), num_raid_devices); + } else { + components_str = g_strdup_printf (_("%d Components (%s each)"), num_raid_devices, s); + } + g_free (s); + + if (raid_size == 0) { + raid_size_str = g_strdup_printf (_("-")); + } else { + raid_size_str = gdu_util_get_size_for_display (raid_size, TRUE); + } + + if (device == NULL) { + if (gdu_activatable_drive_can_activate (activatable_drive)) { + state_str = g_strdup (_("Not running")); + } else if (gdu_activatable_drive_can_activate_degraded (activatable_drive)) { + state_str = g_strdup (_("Not running, can only start degraded")); + } else { + state_str = g_strdup (_("Not running, not enough components to start")); + } + } else { + gboolean is_degraded; + const char *sync_action; + double sync_percentage; + guint64 sync_speed; + char *sync_speed_str; + GString *str; + + is_degraded = gdu_device_linux_md_is_degraded (device); + sync_action = gdu_device_linux_md_get_sync_action (device); + sync_percentage = gdu_device_linux_md_get_sync_percentage (device); + sync_speed = gdu_device_linux_md_get_sync_speed (device); + + str = g_string_new (NULL); + if (is_degraded) + g_string_append (str, _("<span foreground='red'><b>Degraded</b></span>")); + else + g_string_append (str, _("Running")); + + if (strcmp (sync_action, "idle") != 0) { + if (strcmp (sync_action, "reshape") == 0) + g_string_append (str, _(", Reshaping")); + else if (strcmp (sync_action, "resync") == 0) + g_string_append (str, _(", Resyncing")); + else if (strcmp (sync_action, "repair") == 0) + g_string_append (str, _(", Repairing")); + else if (strcmp (sync_action, "recover") == 0) + g_string_append (str, _(", Recovering")); + + sync_speed_str = gdu_util_get_speed_for_display (sync_speed); + g_string_append_printf (str, _(" @ %3.01f%% (%s)"), sync_percentage, sync_speed_str); + g_free (sync_speed_str); + } + + state_str = g_string_free (str, FALSE); + } + + gtk_label_set_text (GTK_LABEL (page->priv->linux_md_name_label), name); + gtk_label_set_text (GTK_LABEL (page->priv->linux_md_type_label), level_str); + gtk_label_set_text (GTK_LABEL (page->priv->linux_md_size_label), raid_size_str); + gtk_label_set_text (GTK_LABEL (page->priv->linux_md_components_label), components_str); + gtk_label_set_markup (GTK_LABEL (page->priv->linux_md_state_label), state_str); + + /* only build a new model if rebuilding the page */ + //if (reset_page) { + { + GtkTreeStore *store; + + if (page->priv->linux_md_tree_store != NULL) + g_object_unref (page->priv->linux_md_tree_store); + + store = gtk_tree_store_new (MD_LINUX_N_COLUMNS, + GDK_TYPE_PIXBUF, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_INT, + G_TYPE_STRING); + page->priv->linux_md_tree_store = store; + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), + MD_LINUX_OBJPATH_COLUMN, + GTK_SORT_ASCENDING); + + /* add all slaves */ + for (l = slaves; l != NULL; l = l->next) { + GduDevice *sd = GDU_DEVICE (l->data); + GdkPixbuf *pixbuf; + char *name; + GtkTreeIter iter; + GduPool *pool; + GduPresentable *p; + GduActivableDriveSlaveState slave_state; + const char *slave_state_str; + + pool = gdu_device_get_pool (sd); + p = gdu_pool_get_volume_by_device (pool, sd); + g_object_unref (pool); + + if (p == NULL) { + g_warning ("Cannot find volume for device"); + continue; + } + + s = gdu_presentable_get_name (p); + name = g_strdup_printf ("%s (%s)", s, gdu_device_get_device_file (sd)); + g_free (s); + pixbuf = gdu_util_get_pixbuf_for_presentable (p, GTK_ICON_SIZE_LARGE_TOOLBAR); + + slave_state = gdu_activatable_drive_get_slave_state (activatable_drive, sd); + + switch (slave_state) { + case GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_RUNNING: + slave_state_str = _("Running"); + break; + case GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_RUNNING_SYNCING: + slave_state_str = _("Running, Syncing to array"); + break; + case GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_RUNNING_HOT_SPARE: + slave_state_str = _("Running, Hot Spare"); + break; + case GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_READY: + slave_state_str = _("Ready"); + break; + case GDU_ACTIVATABLE_DRIVE_SLAVE_STATE_NOT_FRESH: + if (device != NULL) { + slave_state_str = _("Not Running, Stale"); + } else { + slave_state_str = _("Stale"); + } + break; + default: + break; + } + + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, + &iter, + MD_LINUX_ICON_COLUMN, pixbuf, + MD_LINUX_NAME_COLUMN, name, + MD_LINUX_STATE_STRING_COLUMN, slave_state_str, + MD_LINUX_STATE_COLUMN, slave_state, + MD_LINUX_OBJPATH_COLUMN, gdu_device_get_object_path (sd), + -1); + + g_free (name); + if (pixbuf != NULL) + g_object_unref (pixbuf); + + g_object_unref (p); + } - s = g_strdup_printf ("uuid=%s\n" - "name=%s\n" - "level=%d\n" - "num_raid_devices=%d\n" - "num_slaves=%d\n" - "device=%p", - uuid, name, level, num_raid_devices, num_slaves, device); + gtk_tree_view_set_model (GTK_TREE_VIEW (page->priv->linux_md_tree_view), + GTK_TREE_MODEL (store)); - gtk_label_set_text (GTK_LABEL (page->priv->linux_md_explanatory_label), s); + gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (page->priv->linux_md_tree_view), + dnd_targets, + num_dnd_targets, + GDK_ACTION_COPY); + + + } + + linux_md_buttons_update (page); out: + g_free (state_str); + g_free (level_str); + g_free (raid_size_str); + g_free (components_str); g_list_foreach (slaves, (GFunc) g_object_unref, NULL); g_list_free (slaves); - g_free (s); if (device != NULL) g_object_unref (device); } diff --git a/src/gdu-page-volume.c b/src/gdu-page-volume.c index 44ed785..1ee1ecc 100644 --- a/src/gdu-page-volume.c +++ b/src/gdu-page-volume.c @@ -725,7 +725,7 @@ update_raid_section (GduPageVolume *page, gboolean reset_page) { char *s; GduDevice *device; - int level; + const char *level; int num_raid_devices; const char *name; const char *raid_name; @@ -740,22 +740,19 @@ update_raid_section (GduPageVolume *page, gboolean reset_page) num_raid_devices = gdu_device_linux_md_component_get_num_raid_devices (device); name = gdu_device_linux_md_component_get_name (device); - switch (level) { - case 0: + + if (strcmp (level, "raid0") == 0) { raid_name = _("RAID-0 array"); - break; - case 1: + } else if (strcmp (level, "raid1") == 0) { raid_name = _("RAID-1 array"); - break; - case 4: + } else if (strcmp (level, "raid4") == 0) { raid_name = _("RAID-4 array"); - break; - case 5: + } else if (strcmp (level, "raid5") == 0) { raid_name = _("RAID-5 array"); - break; - default: + } else if (strcmp (level, "raid6") == 0) { + raid_name = _("RAID-6 array"); + } else { raid_name = _("RAID array"); - break; } if (name == NULL || strlen (name) == 0) { diff --git a/src/gdu-pool.c b/src/gdu-pool.c index d506a6e..205c9b8 100644 --- a/src/gdu-pool.c +++ b/src/gdu-pool.c @@ -36,8 +36,12 @@ enum { DEVICE_ADDED, DEVICE_REMOVED, + DEVICE_CHANGED, + DEVICE_JOB_CHANGED, PRESENTABLE_ADDED, PRESENTABLE_REMOVED, + PRESENTABLE_CHANGED, + PRESENTABLE_JOB_CHANGED, LAST_SIGNAL, }; @@ -55,7 +59,7 @@ struct _GduPoolPrivate GHashTable *volumes; /* object path -> GduVolume* */ GHashTable *drives; /* object path -> GduDrive* */ - GHashTable *activatable_drives; /* uuid -> GduActivatableDrive* */ + GList *activatable_drives; GHashTable *drive_holes; /* object path -> GList of GduVolumeHole* */ GHashTable *drive_unrecognized; /* object path -> GduVolume* */ @@ -111,7 +115,8 @@ gdu_pool_finalize (GduPool *pool) g_hash_table_unref (pool->priv->volumes); g_hash_table_unref (pool->priv->drives); - g_hash_table_unref (pool->priv->activatable_drives); + g_list_foreach (pool->priv->activatable_drives, (GFunc) g_object_unref, NULL); + g_list_free (pool->priv->activatable_drives); g_hash_table_unref (pool->priv->drive_holes); g_hash_table_unref (pool->priv->drive_unrecognized); @@ -152,6 +157,26 @@ gdu_pool_class_init (GduPoolClass *klass) G_TYPE_NONE, 1, GDU_TYPE_DEVICE); + signals[DEVICE_CHANGED] = + g_signal_new ("device_changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GduPoolClass, device_changed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GDU_TYPE_DEVICE); + + signals[DEVICE_JOB_CHANGED] = + g_signal_new ("device_job_changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GduPoolClass, device_job_changed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GDU_TYPE_DEVICE); + signals[PRESENTABLE_ADDED] = g_signal_new ("presentable_added", G_TYPE_FROM_CLASS (klass), @@ -171,6 +196,26 @@ gdu_pool_class_init (GduPoolClass *klass) g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GDU_TYPE_PRESENTABLE); + + signals[PRESENTABLE_CHANGED] = + g_signal_new ("presentable_changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GduPoolClass, presentable_changed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GDU_TYPE_PRESENTABLE); + + signals[PRESENTABLE_CHANGED] = + g_signal_new ("presentable_job_changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GduPoolClass, presentable_job_changed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GDU_TYPE_PRESENTABLE); } static void @@ -420,7 +465,7 @@ update_whole_disk (GduPool *pool, GduDrive *drive, GduDevice *device, const char /* add volume for whole disk device if it's not there already */ volume = g_hash_table_lookup (pool->priv->volumes, object_path); if (volume == NULL) { - volume = gdu_volume_new_from_device (device, GDU_PRESENTABLE (drive)); + volume = gdu_volume_new_from_device (pool, device, GDU_PRESENTABLE (drive)); g_hash_table_insert (pool->priv->volumes, g_strdup (object_path), g_object_ref (volume)); @@ -431,6 +476,103 @@ update_whole_disk (GduPool *pool, GduDrive *drive, GduDevice *device, const char } } + +static GduActivatableDrive * +find_activatable_drive_for_linux_md_component (GduPool *pool, GduDevice *device) +{ + GList *l; + GduActivatableDrive *ad; + const char *uuid; + + ad = NULL; + + if (!gdu_device_is_linux_md_component (device)) { + g_warning ("not linux md component"); + goto out; + } + + uuid = gdu_device_linux_md_component_get_uuid (device); + + for (l = pool->priv->activatable_drives; l != NULL; l = l->next) { + ad = l->data; + + if (gdu_activatable_drive_has_uuid (ad, uuid)) + goto out; + + if (gdu_activatable_drive_device_references_slave (ad, device)) + goto out; + } + + ad = NULL; + +out: + return ad; +} + +static void +ensure_activatable_drive_for_linux_md_component (GduPool *pool, GduDevice *device) +{ + GduActivatableDrive *activatable_drive; + + activatable_drive = find_activatable_drive_for_linux_md_component (pool, device); + + /* create it unless we have it already */ + if (activatable_drive == NULL) { + activatable_drive = gdu_activatable_drive_new (pool, + GDU_ACTIVATABLE_DRIVE_KIND_LINUX_MD); + + pool->priv->activatable_drives = g_list_prepend (pool->priv->activatable_drives, + g_object_ref (activatable_drive)); + + /* and we're part of the gang */ + pool->priv->presentables = g_list_prepend (pool->priv->presentables, + GDU_PRESENTABLE (activatable_drive)); + g_signal_emit (pool, signals[PRESENTABLE_ADDED], 0, GDU_PRESENTABLE (activatable_drive)); + } + + /* add ourselves to the drive if we're not already part of it */ + if (!gdu_activatable_drive_has_slave (activatable_drive, device)) + gdu_activatable_drive_add_slave (activatable_drive, device); +} + +static GduActivatableDrive * +find_activatable_drive_for_linux_md_array (GduPool *pool, GduDevice *device) +{ + int n; + GList *l; + char **slaves; + GduActivatableDrive *ad; + + ad = NULL; + + if (!gdu_device_is_linux_md (device)) { + g_warning ("not linux md array"); + goto out; + } + + /* TODO: if this is too slow we can optimize by having back-links on GduDevice */ + + slaves = gdu_device_linux_md_get_slaves (device); + for (n = 0; slaves[n] != NULL; n++) { + GduDevice *slave; + + slave = g_hash_table_lookup (pool->priv->devices, slaves[n]); + if (slave == NULL) + continue; + + for (l = pool->priv->activatable_drives; l != NULL; l = l->next) { + ad = l->data; + if (gdu_activatable_drive_has_slave (ad, slave)) + goto out; + } + } + + ad = NULL; + +out: + return ad; +} + static GduDevice * gdu_pool_add_device_by_object_path (GduPool *pool, const char *object_path) { @@ -450,26 +592,41 @@ gdu_pool_add_device_by_object_path (GduPool *pool, const char *object_path) /* we may have a GduActivatableDrive for this sucker already */ if (gdu_device_is_linux_md (device)) { - const char *uuid; GduActivatableDrive *activatable_drive; - uuid = gdu_device_linux_md_get_uuid (device); - activatable_drive = g_hash_table_lookup (pool->priv->activatable_drives, uuid); - if (activatable_drive != NULL) { - /* yup, we do.. and now that GduActivatableDrive got a backing device */ - gdu_activatable_drive_set_device (activatable_drive, device); + activatable_drive = find_activatable_drive_for_linux_md_array (pool, device); + if (activatable_drive == NULL) { + /* No activatable drive.. this can indeed happen when a new array is + * created on the command line; then we get an add for the array + * before the components. + * + * (The components will follow shortly though; this is because + * DeviceKit-disks triggers a change even on all components of + * an array on 'change', 'add' and 'remove' of said array). + */ + + activatable_drive = gdu_activatable_drive_new ( + pool, GDU_ACTIVATABLE_DRIVE_KIND_LINUX_MD); - drive = GDU_DRIVE (activatable_drive); + pool->priv->activatable_drives = g_list_prepend ( + pool->priv->activatable_drives, g_object_ref (activatable_drive)); - /* and now that we have a backing device.. we can part of the drives hash */ - g_hash_table_insert (pool->priv->drives, g_strdup (object_path), - g_object_ref (drive)); } + + /* we've got a backing device */ + gdu_activatable_drive_set_device (activatable_drive, device); + + drive = GDU_DRIVE (activatable_drive); + + /* and now that we have a backing device.. we can part of the drives hash */ + g_hash_table_insert (pool->priv->drives, g_strdup (object_path), + g_object_ref (drive)); + } /* otherwise do create a new GduDrive object */ if (drive == NULL) { - drive = gdu_drive_new_from_device (device); + drive = gdu_drive_new_from_device (pool, device); g_hash_table_insert (pool->priv->drives, g_strdup (object_path), g_object_ref (drive)); pool->priv->presentables = g_list_prepend (pool->priv->presentables, GDU_PRESENTABLE (drive)); g_signal_emit (pool, signals[PRESENTABLE_ADDED], 0, GDU_PRESENTABLE (drive)); @@ -545,7 +702,7 @@ gdu_pool_add_device_by_object_path (GduPool *pool, const char *object_path) } /* add the partition */ - volume = gdu_volume_new_from_device (device, enclosing_presentable); + volume = gdu_volume_new_from_device (pool, device, enclosing_presentable); g_hash_table_insert (pool->priv->volumes, g_strdup (object_path), g_object_ref (volume)); pool->priv->presentables = g_list_prepend (pool->priv->presentables, GDU_PRESENTABLE (volume)); g_signal_emit (pool, signals[PRESENTABLE_ADDED], 0, GDU_PRESENTABLE (volume)); @@ -587,7 +744,7 @@ gdu_pool_add_device_by_object_path (GduPool *pool, const char *object_path) if (enclosing_volume != NULL) { GduVolume *volume; - volume = gdu_volume_new_from_device (device, GDU_PRESENTABLE (enclosing_volume)); + volume = gdu_volume_new_from_device (pool, device, GDU_PRESENTABLE (enclosing_volume)); g_hash_table_insert (pool->priv->volumes, g_strdup (object_path), g_object_ref (volume)); @@ -603,30 +760,7 @@ gdu_pool_add_device_by_object_path (GduPool *pool, const char *object_path) * - TODO: LVM Physical Volumes */ if (gdu_device_is_linux_md_component (device)) { - const char *uuid; - GduActivatableDrive *activatable_drive; - - uuid = gdu_device_linux_md_component_get_uuid (device); - activatable_drive = g_hash_table_lookup (pool->priv->activatable_drives, uuid); - - /* creat it unless we have it already */ - if (activatable_drive == NULL) { - - activatable_drive = gdu_activatable_drive_new (pool, - GDU_ACTIVATABLE_DRIVE_KIND_LINUX_MD, - uuid); - - g_hash_table_insert (pool->priv->activatable_drives, (gpointer) uuid, - g_object_ref (activatable_drive)); - - /* and we're part of the gang */ - pool->priv->presentables = g_list_prepend (pool->priv->presentables, - GDU_PRESENTABLE (activatable_drive)); - g_signal_emit (pool, signals[PRESENTABLE_ADDED], 0, GDU_PRESENTABLE (activatable_drive)); - } - - /* add ourselves to the drive */ - gdu_activatable_drive_add_slave (activatable_drive, device); + ensure_activatable_drive_for_linux_md_component (pool, device); } return device; @@ -641,6 +775,27 @@ device_added_signal_handler (DBusGProxy *proxy, const char *object_path, gpointe } static void +remove_activatable_drive_if_empty (GduPool *pool, GduActivatableDrive *activatable_drive) +{ + if (!gdu_activatable_drive_is_device_set (activatable_drive) && + gdu_activatable_drive_get_num_slaves (activatable_drive) == 0) { + /* no device and no slaves -> remove */ + pool->priv->activatable_drives= g_list_remove ( + pool->priv->activatable_drives, activatable_drive); + g_object_unref (activatable_drive); + + pool->priv->presentables = g_list_remove ( + pool->priv->presentables, + GDU_PRESENTABLE (activatable_drive)); + + g_signal_emit (pool, signals[PRESENTABLE_REMOVED], 0, + GDU_PRESENTABLE (activatable_drive)); + + g_object_unref (activatable_drive); + } +} + +static void device_removed_signal_handler (DBusGProxy *proxy, const char *object_path, gpointer user_data) { GduPool *pool = GDU_POOL (user_data); @@ -660,13 +815,12 @@ device_removed_signal_handler (DBusGProxy *proxy, const char *object_path, gpoin /* set device to NULL for activatable drive if we go away */ if (gdu_device_is_linux_md (device)) { - const char *uuid; GduActivatableDrive *activatable_drive; - uuid = gdu_device_linux_md_get_uuid (device); - activatable_drive = g_hash_table_lookup (pool->priv->activatable_drives, uuid); + activatable_drive = find_activatable_drive_for_linux_md_array (pool, device); if (activatable_drive != NULL) { gdu_activatable_drive_set_device (activatable_drive, NULL); + remove_activatable_drive_if_empty (pool, activatable_drive); } } @@ -686,38 +840,14 @@ device_removed_signal_handler (DBusGProxy *proxy, const char *object_path, gpoin g_object_unref (d); } - /* remove activable drive if the last component goes away */ + /* remove activatable drive if the last component goes away */ if (gdu_device_is_linux_md_component (device)) { - const char *uuid; GduActivatableDrive *activatable_drive; - uuid = gdu_device_linux_md_component_get_uuid (device); - activatable_drive = g_hash_table_lookup (pool->priv->activatable_drives, uuid); + activatable_drive = find_activatable_drive_for_linux_md_component (pool, device); if (activatable_drive != NULL) { - GList *slaves; - gdu_activatable_drive_remove_slave (activatable_drive, device); - slaves = gdu_activatable_drive_get_slaves (activatable_drive); - if (slaves == NULL) { - /* we're the last slave and we're toast; tear down - * the activatable drive - */ - - g_hash_table_remove (pool->priv->activatable_drives, uuid); - - pool->priv->presentables = g_list_remove ( - pool->priv->presentables, - GDU_PRESENTABLE (activatable_drive)); - - g_signal_emit (pool, signals[PRESENTABLE_REMOVED], 0, - GDU_PRESENTABLE (activatable_drive)); - - g_object_unref (activatable_drive); - - } else { - g_list_foreach (slaves, (GFunc) g_object_unref, NULL); - g_list_free (slaves); - } + remove_activatable_drive_if_empty (pool, activatable_drive); } } @@ -734,8 +864,32 @@ device_changed_signal_handler (DBusGProxy *proxy, const char *object_path, gpoin if ((device = gdu_pool_get_by_object_path (pool, object_path)) != NULL) { GduDrive *drive; + GduActivatableDrive *ad_before; + + ad_before = NULL; + if (gdu_device_is_linux_md_component (device)) + ad_before = find_activatable_drive_for_linux_md_component (pool, device); gdu_device_changed (device); + g_signal_emit_by_name (pool, "device-changed", device); + + if (ad_before != NULL) { + GduActivatableDrive *ad; + + ad = find_activatable_drive_for_linux_md_component (pool, device); + if (ad != ad_before) { + /* no longer part of ad_before; remove */ + gdu_activatable_drive_remove_slave (ad_before, device); + remove_activatable_drive_if_empty (pool, ad_before); + } + } + + /* device may have become a linux md component; make sure it's + * added to an Activatable Drive if so + */ + if (gdu_device_is_linux_md_component (device)) { + ensure_activatable_drive_for_linux_md_component (pool, device); + } drive = g_hash_table_lookup (pool->priv->drives, object_path); if (drive != NULL) @@ -748,6 +902,7 @@ device_changed_signal_handler (DBusGProxy *proxy, const char *object_path, gpoin } else { g_warning ("unknown device to on change, object_path='%s'", object_path); } + } static void @@ -774,6 +929,7 @@ device_job_changed_signal_handler (DBusGProxy *proxy, job_cur_task, job_cur_task_id, job_cur_task_percentage); + g_signal_emit_by_name (pool, "device-job-changed", device); } else { g_warning ("unknown device to on job-change, object_path='%s'", object_path); } @@ -812,7 +968,6 @@ gdu_pool_new (void) pool->priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); pool->priv->volumes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); pool->priv->drives = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - pool->priv->activatable_drives = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); pool->priv->drive_holes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_list_of_holes); pool->priv->drive_unrecognized = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); @@ -965,6 +1120,19 @@ gdu_pool_get_enclosed_presentables (GduPool *pool, GduPresentable *presentable) return ret; } +GduPresentable * +gdu_pool_get_volume_by_device (GduPool *pool, GduDevice *device) +{ + GduVolume *volume; + + volume = g_hash_table_lookup (pool->priv->volumes, gdu_device_get_object_path (device)); + if (volume != NULL) + return g_object_ref (volume); + else + return NULL; +} + + /* ---------------------------------------------------------------------------------------------------- */ typedef struct { @@ -979,7 +1147,7 @@ op_assemble_linux_md_array_cb (DBusGProxy *proxy, char *assembled_array_object_p AssembleLinuxMdArrayData *data = user_data; if (error != NULL) { - g_warning ("op_change_secret_for_encrypted_cb failed: %s", error->message); + g_warning ("op_assemble_linux_md_array_cb failed: %s", error->message); data->callback (data->pool, NULL, error, data->user_data); } else { data->callback (data->pool, assembled_array_object_path, error, data->user_data); diff --git a/src/gdu-pool.h b/src/gdu-pool.h index 7655498..dd188bf 100644 --- a/src/gdu-pool.h +++ b/src/gdu-pool.h @@ -53,19 +53,24 @@ struct _GduPoolClass /* signals */ void (*device_added) (GduPool *pool, GduDevice *device); void (*device_removed) (GduPool *pool, GduDevice *device); + void (*device_changed) (GduPool *pool, GduDevice *device); + void (*device_job_changed) (GduPool *pool, GduDevice *device); void (*presentable_added) (GduPool *pool, GduPresentable *presentable); void (*presentable_removed) (GduPool *pool, GduPresentable *presentable); + void (*presentable_changed) (GduPool *pool, GduPresentable *presentable); + void (*presentable_job_changed) (GduPool *pool, GduPresentable *presentable); }; GType gdu_pool_get_type (void); GduPool *gdu_pool_new (void); GduDevice *gdu_pool_get_by_object_path (GduPool *pool, const char *object_path); +GduPresentable *gdu_pool_get_volume_by_device (GduPool *pool, GduDevice *device); + GList *gdu_pool_get_devices (GduPool *pool); GList *gdu_pool_get_presentables (GduPool *pool); GList *gdu_pool_get_enclosed_presentables (GduPool *pool, GduPresentable *presentable); - /* ---------------------------------------------------------------------------------------------------- */ typedef void (*GduPoolAssembleLinuxMdArrayCompletedFunc) (GduPool *pool, diff --git a/src/gdu-shell.c b/src/gdu-shell.c index 0475847..1f7dac4 100644 --- a/src/gdu-shell.c +++ b/src/gdu-shell.c @@ -136,7 +136,7 @@ gdu_shell_get_selected_presentable (GduShell *shell) void gdu_shell_select_presentable (GduShell *shell, GduPresentable *presentable) { - gdu_tree_select_presentable (GTK_TREE_VIEW (shell->priv->treeview), presentable); + gdu_device_tree_select_presentable (GTK_TREE_VIEW (shell->priv->treeview), presentable); gtk_widget_grab_focus (shell->priv->treeview); } @@ -468,27 +468,10 @@ gdu_shell_update (GduShell *shell) if (GDU_IS_ACTIVATABLE_DRIVE (shell->priv->presentable_now_showing)) { GduActivatableDrive *ad = GDU_ACTIVATABLE_DRIVE (shell->priv->presentable_now_showing); - if (device != NULL) { - can_stop = TRUE; - } else { - if (gdu_activatable_drive_get_kind (ad) == GDU_ACTIVATABLE_DRIVE_KIND_LINUX_MD) { - device = gdu_activatable_drive_get_first_slave (ad); - if (device != NULL) { - int num_slaves; - int num_raid_devices; - - num_slaves = gdu_activatable_drive_get_num_slaves (ad); - num_raid_devices = gdu_device_linux_md_component_get_num_raid_devices (device); - - /* TODO: support starting in degraded mode */ - if (num_slaves == num_raid_devices) { - can_start = TRUE; - } - g_object_unref (device); - } - } - } + can_stop = gdu_activatable_drive_is_activated (ad); + can_start = (gdu_activatable_drive_can_activate (ad) || + gdu_activatable_drive_can_activate_degraded (ad)); } if (job_in_progress || last_job_failed) { @@ -560,7 +543,7 @@ device_tree_changed (GtkTreeSelection *selection, gpointer user_data) GtkTreeView *device_tree_view; device_tree_view = gtk_tree_selection_get_tree_view (selection); - presentable = gdu_tree_get_selected_presentable (device_tree_view); + presentable = gdu_device_tree_get_selected_presentable (device_tree_view); if (presentable != NULL) { @@ -608,7 +591,7 @@ presentable_removed (GduPool *pool, GduPresentable *presentable, gpointer user_d gdu_shell_select_presentable (shell, enclosing_presentable); g_object_unref (enclosing_presentable); } else { - gdu_tree_select_first_presentable (GTK_TREE_VIEW (shell->priv->treeview)); + gdu_device_tree_select_first_presentable (GTK_TREE_VIEW (shell->priv->treeview)); gtk_widget_grab_focus (shell->priv->treeview); } } @@ -707,49 +690,21 @@ lock_action_callback (GtkAction *action, gpointer user_data) } } -typedef struct -{ - GduActivatableDrive *activatable_drive; - GduShell *shell; -} StartData; - -static StartData * -start_data_new (GduActivatableDrive *activatable_drive, - GduShell *shell) -{ - StartData *sd; - sd = g_new0 (StartData, 1); - sd->activatable_drive = g_object_ref (activatable_drive); - sd->shell = g_object_ref (shell); - return sd; -} - -static void -start_data_free (StartData *sd) -{ - g_object_unref (sd->activatable_drive); - g_object_unref (sd->shell); - g_free (sd); -} - static void -assembly_completed (GduPool *pool, - const char *assembled_array_object_path, - GError *error, - gpointer user_data) +start_cb (GduActivatableDrive *ad, gboolean success, GError *error, gpointer user_data) { - StartData *sd = user_data; + GduShell *shell = user_data; if (error != NULL) { GtkWidget *dialog; dialog = gtk_message_dialog_new_with_markup ( - GTK_WINDOW (sd->shell->priv->app_window), + GTK_WINDOW (shell->priv->app_window), GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("<big><b>There was an error starting the array \"%s\".</b></big>"), - gdu_presentable_get_name (GDU_PRESENTABLE (sd->activatable_drive))); + gdu_presentable_get_name (GDU_PRESENTABLE (ad))); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), error->message); gtk_dialog_run (GTK_DIALOG (dialog)); @@ -760,21 +715,15 @@ assembly_completed (GduPool *pool, } out: - start_data_free (sd); + g_object_unref (shell); } static void start_action_callback (GtkAction *action, gpointer user_data) { GduShell *shell = user_data; - GduDevice *device; - GPtrArray *components; - GList *l; - GList *slaves; GduActivatableDrive *ad; - device = NULL; - if (!GDU_IS_ACTIVATABLE_DRIVE (shell->priv->presentable_now_showing)) { g_warning ("presentable is not an activatable drive"); goto out; @@ -782,58 +731,67 @@ start_action_callback (GtkAction *action, gpointer user_data) ad = GDU_ACTIVATABLE_DRIVE (shell->priv->presentable_now_showing); - device = gdu_presentable_get_device (shell->priv->presentable_now_showing); - if (device != NULL) { - g_warning ("activatable drive already have a device; refusing to initiate assembly"); + if (gdu_activatable_drive_is_activated (ad)) { + g_warning ("activatable drive already activated; refusing to activate it"); goto out; } - components = g_ptr_array_new (); - slaves = gdu_activatable_drive_get_slaves (ad); - for (l = slaves; l != NULL; l = l->next) { - GduDevice *d = l->data; - /* no need to dup; we keep a ref on d for the lifetime of components */ - g_ptr_array_add (components, (gpointer) gdu_device_get_object_path (d)); - } + /* ask for consent before activating in degraded mode */ + if (!gdu_activatable_drive_can_activate (ad) && + gdu_activatable_drive_can_activate_degraded (ad)) { + GtkWidget *dialog; + int response; - gdu_pool_op_assemble_linux_md_array (shell->priv->pool, - components, - assembly_completed, - start_data_new (ad, shell)); + dialog = gtk_message_dialog_new_with_markup ( + GTK_WINDOW (shell->priv->app_window), + GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_CANCEL, + _("<big><b>Are you sure you want to start the array \"%s\" in degraded mode?</b></big>"), + gdu_presentable_get_name (GDU_PRESENTABLE (ad))); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("Starting a RAID array in degraded mode means that " + "the RAID volume is no longer tolerant to drive " + "failures. Data on the volume may be irrevocably " + "lost if a drive fails.")); - g_ptr_array_free (components, TRUE); - g_list_foreach (slaves, (GFunc) g_object_unref, NULL); - g_list_free (slaves); + gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Start Array"), 0); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + if (response != 0) + goto out; + } + gdu_activatable_drive_activate (ad, + start_cb, + g_object_ref (shell)); out: - if (device != NULL) - g_object_unref (device); + ; } static void stop_action_callback (GtkAction *action, gpointer user_data) { GduShell *shell = user_data; - GduDevice *device; - - device = NULL; + GduActivatableDrive *ad; if (!GDU_IS_ACTIVATABLE_DRIVE (shell->priv->presentable_now_showing)) { g_warning ("presentable is not an activatable drive"); goto out; } - device = gdu_presentable_get_device (shell->priv->presentable_now_showing); - if (device == NULL) { - g_warning ("no device for activatable drive"); + ad = GDU_ACTIVATABLE_DRIVE (shell->priv->presentable_now_showing); + + if (!gdu_activatable_drive_is_activated (ad)) { + g_warning ("activatable drive isn't activated; refusing to deactivate it"); goto out; } - gdu_device_op_stop_linux_md_array (device); - + gdu_activatable_drive_deactivate (ad); out: - if (device != NULL) - g_object_unref (device); + ; } @@ -983,16 +941,12 @@ create_ui_manager (GduShell *shell) shell->priv->pk_mount_action, _("_Unlock"), _("Unlock the encrypted device, making the data available in cleartext")); - /* TODO: the lock-secure and lock-insecure icons are from Epiphany. - * Probably need to ship our own copy. - */ - g_object_set (shell->priv->unlock_action, "auth-label", _("_Unlock..."), - "yes-icon-name", "stock_lock-open", - "no-icon-name", "stock_lock-open", - "auth-icon-name", "stock_lock-open", - "self-blocked-icon-name", "stock_lock-open", + "yes-icon-name", "gdu-encrypted-unlock", + "no-icon-name", "gdu-encrypted-unlock", + "auth-icon-name", "gdu-encrypted-unlock", + "self-blocked-icon-name", "gdu-encrypted-unlock", NULL); g_signal_connect (shell->priv->unlock_action, "activate", G_CALLBACK (unlock_action_callback), shell); gtk_action_group_add_action (shell->priv->action_group, GTK_ACTION (shell->priv->unlock_action)); @@ -1005,10 +959,10 @@ create_ui_manager (GduShell *shell) _("Lock the encrypted device, making the cleartext data unavailable")); g_object_set (shell->priv->lock_action, "auth-label", _("_Lock..."), - "yes-icon-name", "stock_lock", - "no-icon-name", "stock_lock", - "auth-icon-name", "stock_lock", - "self-blocked-icon-name", "stock_lock", + "yes-icon-name", "gdu-encrypted-lock", + "no-icon-name", "gdu-encrypted-lock", + "auth-icon-name", "gdu-encrypted-lock", + "self-blocked-icon-name", "gdu-encrypted-lock", NULL); g_signal_connect (shell->priv->lock_action, "activate", G_CALLBACK (lock_action_callback), shell); gtk_action_group_add_action (shell->priv->action_group, GTK_ACTION (shell->priv->lock_action)); @@ -1022,10 +976,10 @@ create_ui_manager (GduShell *shell) _("Start the array")); g_object_set (shell->priv->start_action, "auth-label", _("_Start..."), - "yes-icon-name", "gtk-media-play-ltr", /* TODO: get suitable icon */ - "no-icon-name", "gtk-media-play-ltr", - "auth-icon-name", "gtk-media-play-ltr", - "self-blocked-icon-name", "gtk-media-play-ltr", + "yes-icon-name", "gdu-raid-array-start", + "no-icon-name", "gdu-raid-array-start", + "auth-icon-name", "gdu-raid-array-start", + "self-blocked-icon-name", "gdu-raid-array-start", NULL); g_signal_connect (shell->priv->start_action, "activate", G_CALLBACK (start_action_callback), shell); gtk_action_group_add_action (shell->priv->action_group, GTK_ACTION (shell->priv->start_action)); @@ -1038,10 +992,10 @@ create_ui_manager (GduShell *shell) _("Stop the array")); g_object_set (shell->priv->stop_action, "auth-label", _("_Stop..."), - "yes-icon-name", "gtk-media-stop", /* TODO: get suitable icon */ - "no-icon-name", "gtk-media-stop", - "auth-icon-name", "gtk-media-stop", - "self-blocked-icon-name", "gtk-media-stop", + "yes-icon-name", "gdu-raid-array-stop", + "no-icon-name", "gdu-raid-array-stop", + "auth-icon-name", "gdu-raid-array-stop", + "self-blocked-icon-name", "gdu-raid-array-stop", NULL); g_signal_connect (shell->priv->stop_action, "activate", G_CALLBACK (stop_action_callback), shell); gtk_action_group_add_action (shell->priv->action_group, GTK_ACTION (shell->priv->stop_action)); @@ -1171,7 +1125,7 @@ create_window (GduShell *shell) GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (treeview_scrolled_window), GTK_SHADOW_IN); - shell->priv->treeview = GTK_WIDGET (gdu_tree_new (shell->priv->pool)); + shell->priv->treeview = gdu_device_tree_new (shell->priv->pool); gtk_container_add (GTK_CONTAINER (treeview_scrolled_window), shell->priv->treeview); /* add pages in a notebook */ @@ -1246,6 +1200,6 @@ create_window (GduShell *shell) gtk_widget_show_all (vbox); - gdu_tree_select_first_presentable (GTK_TREE_VIEW (shell->priv->treeview)); + gdu_device_tree_select_first_presentable (GTK_TREE_VIEW (shell->priv->treeview)); } diff --git a/src/gdu-tree.c b/src/gdu-tree.c index f6285ac..2a0e324 100644 --- a/src/gdu-tree.c +++ b/src/gdu-tree.c @@ -26,6 +26,29 @@ #include "gdu-util.h" #include "gdu-tree.h" + +struct _GduDeviceTreePrivate +{ + GduPresentable *presentable; + GduPool *pool; +}; + +static GObjectClass *parent_class = NULL; + +G_DEFINE_TYPE (GduDeviceTree, gdu_device_tree, GTK_TYPE_TREE_VIEW) + +static void device_tree_presentable_added (GduPool *pool, GduPresentable *presentable, gpointer user_data); +static void device_tree_presentable_removed (GduPool *pool, GduPresentable *presentable, gpointer user_data); +static void device_tree_presentable_changed (GduPool *pool, GduPresentable *presentable, gpointer user_data); +static void add_presentable_to_tree (GduDeviceTree *device_tree, GduPresentable *presentable, GtkTreeIter *iter_out); + +static gint sort_iter_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata); + +enum { + PROP_0, + PROP_POOL, +}; + enum { ICON_COLUMN, @@ -35,6 +58,157 @@ enum N_COLUMNS }; +static void +update_pool (GduDeviceTree *device_tree) +{ + GList *presentables; + GList *l; + + presentables = gdu_pool_get_presentables (device_tree->priv->pool); + for (l = presentables; l != NULL; l = l->next) { + GduPresentable *presentable = GDU_PRESENTABLE (l->data); + add_presentable_to_tree (device_tree, presentable, NULL); + g_object_unref (presentable); + } + g_list_free (presentables); + + /* add/remove/change rows when the pool reports presentable add/remove/change */ + g_signal_connect (device_tree->priv->pool, "presentable-added", + (GCallback) device_tree_presentable_added, device_tree); + g_signal_connect (device_tree->priv->pool, "presentable-removed", + (GCallback) device_tree_presentable_removed, device_tree); + g_signal_connect (device_tree->priv->pool, "presentable-changed", + (GCallback) device_tree_presentable_changed, device_tree); +} + +static void +gdu_device_tree_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GduDeviceTree *device_tree = GDU_DEVICE_TREE (object); + gpointer obj; + + switch (prop_id) { + case PROP_POOL: + if (device_tree->priv->pool != NULL) + g_object_unref (device_tree->priv->pool); + obj = g_value_get_object (value); + device_tree->priv->pool = (obj == NULL ? NULL : g_object_ref (obj)); + update_pool (device_tree); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdu_device_tree_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GduDeviceTree *device_tree = GDU_DEVICE_TREE (object); + + switch (prop_id) { + case PROP_POOL: + g_value_set_object (value, device_tree->priv->pool); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdu_device_tree_finalize (GduDeviceTree *device_tree) +{ + g_signal_handlers_disconnect_by_func (device_tree->priv->pool, device_tree_presentable_added, device_tree); + g_signal_handlers_disconnect_by_func (device_tree->priv->pool, device_tree_presentable_removed, device_tree); + g_signal_handlers_disconnect_by_func (device_tree->priv->pool, device_tree_presentable_changed, device_tree); + + if (device_tree->priv->pool != NULL) + g_object_unref (device_tree->priv->pool); + + if (G_OBJECT_CLASS (parent_class)->finalize) + (* G_OBJECT_CLASS (parent_class)->finalize) (G_OBJECT (device_tree)); +} + +static void +gdu_device_tree_class_init (GduDeviceTreeClass *klass) +{ + GObjectClass *obj_class = (GObjectClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + obj_class->finalize = (GObjectFinalizeFunc) gdu_device_tree_finalize; + obj_class->set_property = gdu_device_tree_set_property; + obj_class->get_property = gdu_device_tree_get_property; + + /** + * GduDeviceTree:pool: + * + * The #GduPool instance we are getting information from + */ + g_object_class_install_property (obj_class, + PROP_POOL, + g_param_spec_object ("pool", + NULL, + NULL, + GDU_TYPE_POOL, + G_PARAM_WRITABLE | + G_PARAM_READABLE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gdu_device_tree_init (GduDeviceTree *device_tree) +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkTreeStore *store; + + device_tree->priv = g_new0 (GduDeviceTreePrivate, 1); + + store = gtk_tree_store_new (N_COLUMNS, + GDK_TYPE_PIXBUF, + G_TYPE_STRING, + GDU_TYPE_PRESENTABLE, + G_TYPE_STRING); + + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store), SORTNAME_COLUMN, sort_iter_compare_func, + NULL, NULL); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), SORTNAME_COLUMN, GTK_SORT_ASCENDING); + + gtk_tree_view_set_model (GTK_TREE_VIEW (device_tree), GTK_TREE_MODEL (store)); + /* TODO: when GTK 2.12 is available... we can do this */ + /*gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (tree), FALSE);*/ + + column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_title (column, "Title"); + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, + "pixbuf", ICON_COLUMN, + NULL); + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, renderer, TRUE); + gtk_tree_view_column_set_attributes (column, renderer, + "text", TITLE_COLUMN, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (device_tree), column); + + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (device_tree), FALSE); + + gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (device_tree), FALSE); + gtk_tree_view_set_level_indentation (GTK_TREE_VIEW (device_tree), 16); + +} + static gint sort_iter_compare_func (GtkTreeModel *model, GtkTreeIter *a, @@ -66,9 +240,9 @@ typedef struct { static gboolean find_iter_by_presentable_foreach (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) { gboolean ret; GduPresentable *presentable = NULL; @@ -109,11 +283,10 @@ find_iter_by_presentable (GtkTreeStore *store, GduPresentable *presentable, GtkT } static void -presentable_changed (GduPresentable *presentable, gpointer user_data) +device_tree_presentable_changed (GduPool *pool, GduPresentable *presentable, gpointer user_data) { GtkTreeView *tree_view = GTK_TREE_VIEW (user_data); char *name; - char *icon_name; GtkTreeStore *store; GtkTreeIter iter; GdkPixbuf *pixbuf; @@ -125,35 +298,9 @@ presentable_changed (GduPresentable *presentable, gpointer user_data) if (find_iter_by_presentable (store, presentable, &iter)) { name = gdu_presentable_get_name (presentable); - icon_name = gdu_presentable_get_icon_name (presentable); device = gdu_presentable_get_device (presentable); - pixbuf = NULL; - if (icon_name != NULL) { - int icon_width, icon_height; - - if (!gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, &icon_height)) - icon_height = 12; - - pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), - icon_name, - icon_height, - GTK_ICON_LOOKUP_GENERIC_FALLBACK, - NULL); - - /* if it's unallocated or unrecognized space, make the icon greyscale */ - if (!gdu_presentable_is_allocated (presentable) || - !gdu_presentable_is_recognized (presentable)) { - GdkPixbuf *pixbuf2; - pixbuf2 = pixbuf; - pixbuf = gdk_pixbuf_copy (pixbuf); - g_object_unref (pixbuf2); - gdk_pixbuf_saturate_and_pixelate (pixbuf, - pixbuf, - 0.0, - FALSE); - } - } + pixbuf = gdu_util_get_pixbuf_for_presentable (presentable, GTK_ICON_SIZE_MENU); gtk_tree_store_set (store, &iter, @@ -162,7 +309,6 @@ presentable_changed (GduPresentable *presentable, gpointer user_data) -1); g_free (name); - g_free (icon_name); if (pixbuf != NULL) g_object_unref (pixbuf); if (device != NULL) @@ -171,14 +317,13 @@ presentable_changed (GduPresentable *presentable, gpointer user_data) } static void -add_presentable_to_tree (GtkTreeView *tree_view, GduPresentable *presentable, GtkTreeIter *iter_out) +add_presentable_to_tree (GduDeviceTree *device_tree, GduPresentable *presentable, GtkTreeIter *iter_out) { GtkTreeIter iter; GtkTreeIter iter2; GtkTreeIter *parent_iter; GdkPixbuf *pixbuf; char *name; - char *icon_name; GtkTreeStore *store; GduDevice *device; GduPresentable *enclosing_presentable; @@ -187,7 +332,7 @@ add_presentable_to_tree (GtkTreeView *tree_view, GduPresentable *presentable, Gt device = NULL; - store = GTK_TREE_STORE (gtk_tree_view_get_model (tree_view)); + store = GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (device_tree))); /* check to see if presentable is already added */ if (find_iter_by_presentable (store, presentable, NULL)) @@ -201,7 +346,7 @@ add_presentable_to_tree (GtkTreeView *tree_view, GduPresentable *presentable, Gt parent_iter = &iter2; } else { /* add parent if it's not already added */ - add_presentable_to_tree (tree_view, enclosing_presentable, &iter2); + add_presentable_to_tree (device_tree, enclosing_presentable, &iter2); parent_iter = &iter2; } g_object_unref (enclosing_presentable); @@ -215,33 +360,7 @@ add_presentable_to_tree (GtkTreeView *tree_view, GduPresentable *presentable, Gt /* compute the name */ name = gdu_presentable_get_name (presentable); - icon_name = gdu_presentable_get_icon_name (presentable); - pixbuf = NULL; - if (icon_name != NULL) { - int icon_width, icon_height; - - if (!gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, &icon_height)) - icon_height = 12; - - pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), - icon_name, - icon_height, - GTK_ICON_LOOKUP_GENERIC_FALLBACK, - NULL); - - /* if it's unallocated or unrecognized space, make the icon greyscale */ - if (!gdu_presentable_is_allocated (presentable) || - !gdu_presentable_is_recognized (presentable)) { - GdkPixbuf *pixbuf2; - pixbuf2 = pixbuf; - pixbuf = gdk_pixbuf_copy (pixbuf); - g_object_unref (pixbuf2); - gdk_pixbuf_saturate_and_pixelate (pixbuf, - pixbuf, - 0.0, - FALSE); - } - } + pixbuf = gdu_util_get_pixbuf_for_presentable (presentable, GTK_ICON_SIZE_MENU); /* sort by offset so we get partitions in the right order */ sortname = g_strdup_printf ("%016lld_%s", gdu_presentable_get_offset (presentable), object_path); @@ -259,21 +378,18 @@ add_presentable_to_tree (GtkTreeView *tree_view, GduPresentable *presentable, Gt *iter_out = iter; g_free (name); - g_free (icon_name); if (pixbuf != NULL) g_object_unref (pixbuf); if (parent_iter != NULL) { GtkTreePath *path; path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), parent_iter); - if (tree_view != NULL && path != NULL) { - gtk_tree_view_expand_row (tree_view, path, TRUE); + if (path != NULL) { + gtk_tree_view_expand_row (GTK_TREE_VIEW (device_tree), path, TRUE); gtk_tree_path_free (path); } } - g_signal_connect (presentable, "changed", (GCallback) presentable_changed, tree_view); - out: if (device != NULL) g_object_unref (device); @@ -282,87 +398,31 @@ out: static void device_tree_presentable_added (GduPool *pool, GduPresentable *presentable, gpointer user_data) { - GtkTreeView *tree_view = GTK_TREE_VIEW (user_data); - add_presentable_to_tree (tree_view, presentable, NULL); + GduDeviceTree *device_tree = GDU_DEVICE_TREE (user_data); + add_presentable_to_tree (device_tree, presentable, NULL); } static void device_tree_presentable_removed (GduPool *pool, GduPresentable *presentable, gpointer user_data) { + GduDeviceTree *device_tree = GDU_DEVICE_TREE (user_data); GtkTreeIter iter; GtkTreeStore *store; - GtkTreeView *tree_view = GTK_TREE_VIEW (user_data); - store = GTK_TREE_STORE (gtk_tree_view_get_model (tree_view)); + store = GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (device_tree))); if (find_iter_by_presentable (store, presentable, &iter)) { gtk_tree_store_remove (store, &iter); - g_signal_handlers_disconnect_by_func (presentable, (GCallback) presentable_changed, tree_view); } } -GtkTreeView * -gdu_tree_new (GduPool *pool) +GtkWidget * +gdu_device_tree_new (GduPool *pool) { - GtkCellRenderer *renderer; - GtkTreeViewColumn *column; - GtkTreeView *tree_view; - GtkTreeStore *store; - GList *presentables; - GList *l; - - store = gtk_tree_store_new (N_COLUMNS, - GDK_TYPE_PIXBUF, - G_TYPE_STRING, - GDU_TYPE_PRESENTABLE, - G_TYPE_STRING); - - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store), SORTNAME_COLUMN, sort_iter_compare_func, - NULL, NULL); - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), SORTNAME_COLUMN, GTK_SORT_ASCENDING); - - tree_view = GTK_TREE_VIEW (gtk_tree_view_new_with_model (GTK_TREE_MODEL (store))); - /* TODO: when GTK 2.12 is available... we can do this */ - /*gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (tree), FALSE);*/ - - column = gtk_tree_view_column_new (); - gtk_tree_view_column_set_title (column, "Title"); - renderer = gtk_cell_renderer_pixbuf_new (); - gtk_tree_view_column_pack_start (column, renderer, FALSE); - gtk_tree_view_column_set_attributes (column, renderer, - "pixbuf", ICON_COLUMN, - NULL); - renderer = gtk_cell_renderer_text_new (); - gtk_tree_view_column_pack_start (column, renderer, TRUE); - gtk_tree_view_column_set_attributes (column, renderer, - "text", TITLE_COLUMN, - NULL); - gtk_tree_view_append_column (tree_view, column); - - gtk_tree_view_set_headers_visible (tree_view, FALSE); - - presentables = gdu_pool_get_presentables (pool); - for (l = presentables; l != NULL; l = l->next) { - GduPresentable *presentable = GDU_PRESENTABLE (l->data); - add_presentable_to_tree (tree_view, presentable, NULL); - g_object_unref (presentable); - } - g_list_free (presentables); - - gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (tree_view), FALSE); - gtk_tree_view_set_level_indentation (GTK_TREE_VIEW (tree_view), 16); - - /* expand all rows after the treeview widget has been realized */ - //g_signal_connect (tree_view, "realize", G_CALLBACK (gtk_tree_view_expand_all), NULL); - - /* add / remove rows when hal reports presentable add / remove */ - g_signal_connect (pool, "presentable_added", (GCallback) device_tree_presentable_added, tree_view); - g_signal_connect (pool, "presentable_removed", (GCallback) device_tree_presentable_removed, tree_view); - - return tree_view; + return GTK_WIDGET (g_object_new (GDU_TYPE_DEVICE_TREE, "pool", pool, NULL)); } GduPresentable * -gdu_tree_get_selected_presentable (GtkTreeView *tree_view) +gdu_device_tree_get_selected_presentable (GtkTreeView *tree_view) { GduPresentable *presentable; GtkTreePath *path; @@ -393,7 +453,7 @@ gdu_tree_get_selected_presentable (GtkTreeView *tree_view) } void -gdu_tree_select_presentable (GtkTreeView *tree_view, GduPresentable *presentable) +gdu_device_tree_select_presentable (GtkTreeView *tree_view, GduPresentable *presentable) { GtkTreePath *path; GtkTreeModel *tree_model; @@ -417,7 +477,7 @@ out: } void -gdu_tree_select_first_presentable (GtkTreeView *tree_view) +gdu_device_tree_select_first_presentable (GtkTreeView *tree_view) { GtkTreePath *path; GtkTreeModel *tree_model; diff --git a/src/gdu-tree.h b/src/gdu-tree.h index 9e0d218..917e4bf 100644 --- a/src/gdu-tree.h +++ b/src/gdu-tree.h @@ -27,9 +27,37 @@ #include "gdu-pool.h" -GtkTreeView *gdu_tree_new (GduPool *pool); -GduPresentable *gdu_tree_get_selected_presentable (GtkTreeView *tree_view); -void gdu_tree_select_presentable (GtkTreeView *tree_view, GduPresentable *presentable); -void gdu_tree_select_first_presentable (GtkTreeView *tree_view); +#define GDU_TYPE_DEVICE_TREE (gdu_device_tree_get_type ()) +#define GDU_DEVICE_TREE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDU_TYPE_DEVICE_TREE, GduDeviceTree)) +#define GDU_DEVICE_TREE_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), GDU_DEVICE_TREE, GduDeviceTreeClass)) +#define GDU_IS_DEVICE_TREE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDU_TYPE_DEVICE_TREE)) +#define GDU_IS_DEVICE_TREE_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), GDU_TYPE_DEVICE_TREE)) +#define GDU_DEVICE_TREE_GET_CLASS (G_TYPE_INSTANCE_GET_CLASS ((obj), GDU_TYPE_DEVICE_TREE, GduDeviceTreeClass)) + +typedef struct _GduDeviceTreeClass GduDeviceTreeClass; +typedef struct _GduDeviceTree GduDeviceTree; + +struct _GduDeviceTreePrivate; +typedef struct _GduDeviceTreePrivate GduDeviceTreePrivate; + +struct _GduDeviceTree +{ + GtkTreeView parent; + + /* private */ + GduDeviceTreePrivate *priv; +}; + +struct _GduDeviceTreeClass +{ + GtkTreeViewClass parent_class; +}; + + +GType gdu_device_tree_get_type (void); +GtkWidget *gdu_device_tree_new (GduPool *pool); +GduPresentable *gdu_device_tree_get_selected_presentable (GtkTreeView *tree_view); +void gdu_device_tree_select_presentable (GtkTreeView *tree_view, GduPresentable *presentable); +void gdu_device_tree_select_first_presentable (GtkTreeView *tree_view); #endif /* GNOME_DISK_UTILITY_TREE_H */ diff --git a/src/gdu-util.c b/src/gdu-util.c index 0059d8b..49ad94f 100644 --- a/src/gdu-util.c +++ b/src/gdu-util.c @@ -1819,3 +1819,43 @@ gdu_util_delete_confirmation_dialog (GtkWidget *parent_window, out: return secure_erase; } + +GdkPixbuf * +gdu_util_get_pixbuf_for_presentable (GduPresentable *presentable, GtkIconSize size) +{ + char *icon_name; + GdkPixbuf *pixbuf; + + icon_name = gdu_presentable_get_icon_name (presentable); + + pixbuf = NULL; + if (icon_name != NULL) { + int icon_width, icon_height; + + if (!gtk_icon_size_lookup (size, &icon_width, &icon_height)) + icon_height = 48; + + pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), + icon_name, + icon_height, + GTK_ICON_LOOKUP_GENERIC_FALLBACK, + NULL); + + /* if it's unallocated or unrecognized space, make the icon greyscale */ + if (!gdu_presentable_is_allocated (presentable) || + !gdu_presentable_is_recognized (presentable)) { + GdkPixbuf *pixbuf2; + pixbuf2 = pixbuf; + pixbuf = gdk_pixbuf_copy (pixbuf); + g_object_unref (pixbuf2); + gdk_pixbuf_saturate_and_pixelate (pixbuf, + pixbuf, + 0.0, + FALSE); + } + } + + g_free (icon_name); + + return pixbuf; +} diff --git a/src/gdu-util.h b/src/gdu-util.h index a475d00..b926f69 100644 --- a/src/gdu-util.h +++ b/src/gdu-util.h @@ -124,5 +124,6 @@ gboolean gdu_util_delete_secret (GduDevice *device); gboolean gdu_util_have_secret (GduDevice *device); +GdkPixbuf *gdu_util_get_pixbuf_for_presentable (GduPresentable *presentable, GtkIconSize size); #endif /* GDU_UTIL_H */ diff --git a/src/gdu-volume.c b/src/gdu-volume.c index 0dfb76e..4d41f0c 100644 --- a/src/gdu-volume.c +++ b/src/gdu-volume.c @@ -33,6 +33,7 @@ struct _GduVolumePrivate { GduDevice *device; + GduPool *pool; GduPresentable *enclosing_presentable; }; @@ -57,6 +58,9 @@ gdu_volume_finalize (GduVolume *volume) g_object_unref (volume->priv->device); } + if (volume->priv->pool != NULL) + g_object_unref (volume->priv->pool); + if (volume->priv->enclosing_presentable != NULL) g_object_unref (volume->priv->enclosing_presentable); @@ -85,6 +89,7 @@ device_changed (GduDevice *device, gpointer user_data) { GduVolume *volume = GDU_VOLUME (user_data); g_signal_emit_by_name (volume, "changed"); + g_signal_emit_by_name (volume->priv->pool, "presentable-changed", volume); } static void @@ -92,6 +97,7 @@ device_job_changed (GduDevice *device, gpointer user_data) { GduVolume *volume = GDU_VOLUME (user_data); g_signal_emit_by_name (volume, "job-changed"); + g_signal_emit_by_name (volume->priv->pool, "presentable-job-changed", volume); } static void @@ -102,12 +108,13 @@ device_removed (GduDevice *device, gpointer user_data) } GduVolume * -gdu_volume_new_from_device (GduDevice *device, GduPresentable *enclosing_presentable) +gdu_volume_new_from_device (GduPool *pool, GduDevice *device, GduPresentable *enclosing_presentable) { GduVolume *volume; volume = GDU_VOLUME (g_object_new (GDU_TYPE_VOLUME, NULL)); volume->priv->device = g_object_ref (device); + volume->priv->pool = g_object_ref (pool); volume->priv->enclosing_presentable = enclosing_presentable != NULL ? g_object_ref (enclosing_presentable) : NULL; diff --git a/src/gdu-volume.h b/src/gdu-volume.h index 42381ea..3bda28d 100644 --- a/src/gdu-volume.h +++ b/src/gdu-volume.h @@ -51,6 +51,6 @@ struct _GduVolumeClass }; GType gdu_volume_get_type (void); -GduVolume *gdu_volume_new_from_device (GduDevice *volume, GduPresentable *enclosing_presentable); +GduVolume *gdu_volume_new_from_device (GduPool *pool, GduDevice *volume, GduPresentable *enclosing_presentable); #endif /* GDU_VOLUME_H */ |
