ui/ncurses: Add device hierarchy
authorSamuel Mendoza-Jonas <sam.mj@au1.ibm.com>
Wed, 16 Jul 2014 06:23:59 +0000 (16:23 +1000)
committerJeremy Kerr <jk@ozlabs.org>
Tue, 22 Jul 2014 09:24:15 +0000 (17:24 +0800)
Boot options are now listed under their matching boot device in the
ncurses UI to help differentitate similar boot option names

Signed-off-by: Samuel Mendoza-Jonas <sam.mj@au1.ibm.com>
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
ui/ncurses/nc-cui.c
ui/ncurses/nc-menu.c
ui/ncurses/nc-menu.h

index 1f163b9ed10f3323a016a28c64bc5d71d96f8c32..9d788fe551aebbd1aebdf4a9a02a3b284e0e74e8 100644 (file)
@@ -405,12 +405,14 @@ static void cui_handle_resize(struct cui *cui)
 static int cui_boot_option_add(struct device *dev, struct boot_option *opt,
                void *arg)
 {
+       struct pmenu_item *i, *dev_hdr = NULL;
        struct cui *cui = cui_from_arg(arg);
        struct cui_opt_data *cod;
+       const char *tab = "  ";
        unsigned int insert_pt;
        int result, rows, cols;
-       struct pmenu_item *i;
        ITEM *selected;
+       char *name;
 
        pb_debug("%s: %p %s\n", __func__, opt, opt->id);
 
@@ -420,9 +422,16 @@ static int cui_boot_option_add(struct device *dev, struct boot_option *opt,
        if (cui->current == &cui->main->scr)
                nc_scr_unpost(cui->current);
 
-       /* Save the item in opt->ui_info for cui_device_remove() */
+       /* Check if the boot device is new */
+       dev_hdr = pmenu_find_device(cui->main, dev, opt);
+
+       /* All actual boot entries are 'tabbed' across */
+       name = talloc_asprintf(cui->main, "%s%s",
+                       tab, opt->name ? : "Unknown Name");
 
-       opt->ui_info = i = pmenu_item_create(cui->main, opt->name);
+       /* Save the item in opt->ui_info for cui_device_remove() */
+       opt->ui_info = i = pmenu_item_create(cui->main, name);
+       talloc_free(name);
        if (!i)
                return -1;
 
@@ -448,8 +457,16 @@ static int cui_boot_option_add(struct device *dev, struct boot_option *opt,
                pb_log("%s: set_menu_items failed: %d\n", __func__, result);
 
        /* Insert new items at insert_pt. */
-       insert_pt = pmenu_grow(cui->main, 1);
-       pmenu_item_insert(cui->main, i, insert_pt);
+       if (dev_hdr) {
+               insert_pt = pmenu_grow(cui->main, 2);
+               pmenu_item_insert(cui->main, dev_hdr, insert_pt);
+               pb_log("%s: adding new device hierarchy %s\n",
+                       __func__,opt->device_id);
+               pmenu_item_insert(cui->main, i, insert_pt+1);
+       } else {
+               insert_pt = pmenu_grow(cui->main, 1);
+               pmenu_item_add(cui->main, i, insert_pt);
+       }
 
        pb_log("%s: adding opt '%s'\n", __func__, cod->name);
        pb_log("   image  '%s'\n", cod->bd->image);
@@ -499,8 +516,9 @@ static int cui_boot_option_add(struct device *dev, struct boot_option *opt,
 static void cui_device_remove(struct device *dev, void *arg)
 {
        struct cui *cui = cui_from_arg(arg);
-       int result;
        struct boot_option *opt;
+       unsigned int i;
+       int result;
 
        pb_log("%s: %p %s\n", __func__, dev, dev->id);
 
@@ -515,10 +533,21 @@ static void cui_device_remove(struct device *dev, void *arg)
                pb_log("%s: set_menu_items failed: %d\n", __func__, result);
 
        list_for_each_entry(&dev->boot_options, opt, list) {
-               struct pmenu_item *i = pmenu_item_from_arg(opt->ui_info);
+               struct pmenu_item *item = pmenu_item_from_arg(opt->ui_info);
+
+               assert(pb_protocol_device_cmp(dev, cod_from_item(item)->dev));
+               pmenu_remove(cui->main, item);
+       }
+
+       /* Manually remove remaining device hierarchy item */
+       for (i=0; i < cui->main->item_count; i++) {
+               struct pmenu_item *item = item_userptr(cui->main->items[i]);
+               if (!item || !item->data )
+                       continue;
 
-               assert(pb_protocol_device_cmp(dev, cod_from_item(i)->dev));
-               pmenu_remove(cui->main, i);
+               struct cui_opt_data *data = item->data;
+               if (data && data->dev && data->dev == dev)
+                       pmenu_remove(cui->main,item);
        }
 
        /* Re-attach the items array. */
index 7d4442b8102d739f00cdbe99f10a88b160a3c96f..a5794f7a63608067a7de2839ea1bee342dcd6161 100644 (file)
@@ -25,6 +25,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <wctype.h>
+#include <util/util.h>
 
 #include "log/log.h"
 #include "talloc/talloc.h"
@@ -163,6 +164,160 @@ void pmenu_item_insert(struct pmenu *menu, struct pmenu_item *item,
        menu->items[index] = item->nci;
 }
 
+/**
+ * pmenu_item_add - Insert item into appropriate position
+ *
+ * Inserts boot entry under matching, predefined device header entry,
+ * moving items in the list if necessary
+ */
+
+void pmenu_item_add(struct pmenu *menu, struct pmenu_item *item,
+       unsigned int insert_pt)
+{
+       struct cui_opt_data *cod = item->data;
+       bool found = false;
+       unsigned int dev;
+
+       /* Items array should already be disconnected */
+
+       for (dev = 0; dev < menu->item_count; dev++) {
+               if (!menu->items[dev])
+                       continue;
+
+               struct pmenu_item *i = item_userptr(menu->items[dev]);
+               struct cui_opt_data *d = i->data;
+               /* Device header will have opt == NULL */
+               if (d && !d->opt) {
+                       if (cod->dev == d->dev) {
+                               found = true;
+                               break;
+                       }
+               }
+       }
+
+       if (found) {
+               assert(dev < insert_pt);
+               /* Shift down entries between header and insert_pt */
+               memmove(menu->items + dev + 2, menu->items + dev + 1,
+                       ((menu->items + insert_pt) - (menu->items + dev + 1))
+                       * sizeof(menu->items[0]));
+               memset(menu->items + dev + 1, 0, sizeof(menu->items[0]));
+               insert_pt = dev + 1;
+       }
+       /* If for some reason we didn't find the matching device,
+        * at least add it to a valid position */
+       pmenu_item_insert(menu, item, insert_pt);
+}
+
+/**
+ * pmenu_find_device - Determine if a boot option is new, and if
+ * so return a new pmenu_item to represent its parent device
+ */
+
+struct pmenu_item *pmenu_find_device(struct pmenu *menu, struct device *dev,
+       struct boot_option *opt)
+{
+       struct pmenu_item *item, *dev_hdr = NULL;
+       struct cui *cui = cui_from_pmenu(menu);
+       bool newdev = true, matched = false;
+       struct interface_info *intf;
+       struct blockdev_info *bd;
+       struct cui_opt_data *cod;
+       struct system_info *sys;
+       char hwaddr[32];
+       unsigned int i;
+       char buf[256];
+
+       for (i = 0; i < menu->item_count; i++) {
+               item = item_userptr(menu->items[i]);
+               cod = item->data;
+               /* boot entries will have opt defined */
+               if (!cod || cod->opt)
+                       continue;
+               if (cod->dev == dev) {
+                       pb_debug("%s: opt %s fits under %s\n",__func__,
+                                opt->name, opt->device_id);
+                       newdev = false;
+                       break;
+               }
+       }
+
+       if (!newdev) {
+               pb_debug("%s: No new device\n",__func__);
+               return NULL;
+       }
+
+       /* Create a dummy pmenu_item to represent the dev */
+       pb_debug("%s: Building new item\n",__func__);
+       sys = cui->sysinfo;
+       switch (dev->type) {
+       case DEVICE_TYPE_OPTICAL:
+       case DEVICE_TYPE_DISK:
+               /* Find block info */
+               for (i = 0; sys && i < sys->n_blockdevs; i++) {
+                       bd = sys->blockdevs[i];
+                       if (!strcmp(opt->device_id, bd->name)) {
+                               matched = true;
+                               break;
+                       }
+               }
+               if (matched) {
+                       snprintf(buf,sizeof(buf),"[%s: %s / %s]",
+                               dev->type == DEVICE_TYPE_DISK ?
+                               "Disk" : "CD/DVD",
+                               bd->name, bd->uuid);
+               }
+               break;
+
+       case DEVICE_TYPE_NETWORK:
+               /* Find interface info */
+               for (i = 0; sys && i < sys->n_interfaces; i++) {
+                       intf = sys->interfaces[i];
+                       if (!strcmp(opt->device_id, intf->name)) {
+                               matched = true;
+                               break;
+                       }
+               }
+               if (matched) {
+                       mac_str(intf->hwaddr, intf->hwaddr_size,
+                               hwaddr, sizeof(hwaddr));
+                       snprintf(buf,sizeof(buf),"[Interface %s / %s]",
+                               intf->name, hwaddr);
+               }
+               break;
+
+       default:
+               /* Assume the device may be able to boot */
+               break;
+       }
+       if (!matched) {
+               pb_debug("%s: No matching device found for %s (%s)\n",
+                       __func__,opt->device_id, dev->id);
+               snprintf(buf,sizeof(buf),"[Unknown Device: %s]",
+                       dev->id);
+       }
+
+       dev_hdr = pmenu_item_create(menu, buf);
+       if (!dev_hdr) {
+               pb_log("%s: Failed to create item\n",__func__);
+               return NULL;
+       }
+
+       dev_hdr->on_execute = NULL;
+       item_opts_off(dev_hdr->nci, O_SELECTABLE);
+
+       /* We identify dev_hdr items as having a valid c->name,
+        * but a NULL c->opt */
+       cod = talloc(dev_hdr, struct cui_opt_data);
+       cod->name = talloc_strdup(dev_hdr, opt->device_id);
+       cod->dev = dev;
+       cod->opt = NULL;
+       dev_hdr->data = cod;
+
+       pb_debug("%s: returning %s\n",__func__,cod->name);
+       return dev_hdr;
+}
+
 static int pmenu_item_get_index(const struct pmenu_item *item)
 {
        unsigned int i;
index 8e9c56b7a728ecd2e6637a1a0f491a5579354057..12eafaf7650c5f902723506acd7abb8a93a1f7bb 100644 (file)
@@ -54,8 +54,12 @@ struct pmenu_item {
 };
 
 struct pmenu_item *pmenu_item_create(struct pmenu *menu, const char *name);
+struct pmenu_item *pmenu_find_device(struct pmenu *menu, struct device *dev,
+       struct boot_option *opt);
 void pmenu_item_insert(struct pmenu *menu, struct pmenu_item *item,
        unsigned int index);
+void pmenu_item_add(struct pmenu *menu, struct pmenu_item *item,
+       unsigned int insert_pt);
 void pmenu_item_delete(struct pmenu_item *item);
 
 static inline struct pmenu_item *pmenu_item_from_arg(void *arg)