summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDavid Zeuthen <davidz@redhat.com>2008-04-04 23:11:52 -0400
committerDavid Zeuthen <davidz@redhat.com>2008-04-04 23:11:52 -0400
commit9e4e5b2a1d59a8a3894632aedb595e3603239249 (patch)
tree2247ef280365a02f8751b8cacc51b8daeeee595c /src
parent93f11e605b8e91d1f95ec743af654ed18a743807 (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.c559
-rw-r--r--src/gdu-activatable-drive.h56
-rw-r--r--src/gdu-device.c165
-rw-r--r--src/gdu-device.h20
-rw-r--r--src/gdu-drive.c9
-rw-r--r--src/gdu-drive.h2
-rw-r--r--src/gdu-page-drive.c841
-rw-r--r--src/gdu-page-volume.c21
-rw-r--r--src/gdu-pool.c312
-rw-r--r--src/gdu-pool.h7
-rw-r--r--src/gdu-shell.c180
-rw-r--r--src/gdu-tree.c330
-rw-r--r--src/gdu-tree.h36
-rw-r--r--src/gdu-util.c40
-rw-r--r--src/gdu-util.h1
-rw-r--r--src/gdu-volume.c9
-rw-r--r--src/gdu-volume.h2
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 */