From a06004f4c668abd7c760a2818d0a8205da7568e7 Mon Sep 17 00:00:00 2001
From: Alexander Graf <agraf@suse.de>
Date: Tue, 26 Apr 2016 15:29:25 +0200
Subject: [PATCH v3] Add hidden menu entries

The menu infrastructure is quite powerful. It allows you to define menu
entries that can contain arbitrary grub commands that can do a lot more
than just boot kernel entries.

For some of these it makes sense to hide them inside the normal menu
though and instead have them available through hotkeys that get advertised
differently. My main use case is to switch to the serial console when
gfxterm is loaded.

So this patch adds support for hidden menu entries that are accessible
using hotkeys, but are not accessible in the grub menu.

Signed-off-by: Alexander Graf <agraf@suse.de>

---

v1 -> v2:

  - fix default entry selection

v2 -> v3:

  - replace "--hidden" parameter with new command "hiddenentry"

--- a/grub-core/commands/legacycfg.c
+++ b/grub-core/commands/legacycfg.c
@@ -143,7 +143,7 @@
 	    args[0] = oldname;
 	    grub_normal_add_menu_entry (1, args, NULL, NULL, "legacy",
 					NULL, NULL,
-					entrysrc, 0);
+					entrysrc, 0, 0);
 	    grub_free (args);
 	    entrysrc[0] = 0;
 	    grub_free (oldname);
@@ -205,7 +205,7 @@
 	}
       args[0] = entryname;
       grub_normal_add_menu_entry (1, args, NULL, NULL, NULL,
-				  NULL, NULL, entrysrc, 0);
+				  NULL, NULL, entrysrc, 0, 0);
       grub_free (args);
     }
 
--- a/grub-core/commands/menuentry.c
+++ b/grub-core/commands/menuentry.c
@@ -78,7 +78,7 @@
 			    char **classes, const char *id,
 			    const char *users, const char *hotkey,
 			    const char *prefix, const char *sourcecode,
-			    int submenu)
+			    int submenu, int hidden)
 {
   int menu_hotkey = 0;
   char **menu_args = NULL;
@@ -188,8 +188,11 @@
   (*last)->args = menu_args;
   (*last)->sourcecode = menu_sourcecode;
   (*last)->submenu = submenu;
+  (*last)->hidden = hidden;
+
+  if (!hidden)
+    menu->size++;
 
-  menu->size++;
   return GRUB_ERR_NONE;
 
  fail:
@@ -286,7 +289,8 @@
 				       users,
 				       ctxt->state[2].arg, 0,
 				       ctxt->state[3].arg,
-				       ctxt->extcmd->cmd->name[0] == 's');
+				       ctxt->extcmd->cmd->name[0] == 's',
+				       ctxt->extcmd->cmd->name[0] == 'h');
 
   src = args[argc - 1];
   args[argc - 1] = NULL;
@@ -303,7 +307,8 @@
 				  ctxt->state[0].args, ctxt->state[4].arg,
 				  users,
 				  ctxt->state[2].arg, prefix, src + 1,
-				  ctxt->extcmd->cmd->name[0] == 's');
+				  ctxt->extcmd->cmd->name[0] == 's',
+				  ctxt->extcmd->cmd->name[0] == 'h');
 
   src[len - 1] = ch;
   args[argc - 1] = src;
@@ -311,7 +316,7 @@
   return r;
 }
 
-static grub_extcmd_t cmd, cmd_sub;
+static grub_extcmd_t cmd, cmd_sub, cmd_hidden;
 
 void
 grub_menu_init (void)
@@ -327,6 +332,13 @@
 				  | GRUB_COMMAND_FLAG_EXTRACTOR,
 				  N_("BLOCK"), N_("Define a submenu."),
 				  options);
+  cmd_hidden = grub_register_extcmd ("hiddenentry", grub_cmd_menuentry,
+				     GRUB_COMMAND_FLAG_BLOCKS
+				     | GRUB_COMMAND_ACCEPT_DASH
+				     | GRUB_COMMAND_FLAG_EXTRACTOR,
+				     N_("BLOCK"),
+				     N_("Define a hidden menu entry."),
+				     options);
 }
 
 void
--- a/grub-core/normal/menu.c
+++ b/grub-core/normal/menu.c
@@ -40,6 +40,8 @@
 grub_err_t (*grub_gfxmenu_try_hook) (int entry, grub_menu_t menu,
 				     int nested) = NULL;
 
+#define MENU_INCLUDE_HIDDEN 0x10000
+
 enum timeout_style {
   TIMEOUT_STYLE_MENU,
   TIMEOUT_STYLE_COUNTDOWN,
@@ -80,8 +82,20 @@
 {
   grub_menu_entry_t e;
 
-  for (e = menu->entry_list; e && no > 0; e = e->next, no--)
-    ;
+  if (no & MENU_INCLUDE_HIDDEN) {
+    no &= ~MENU_INCLUDE_HIDDEN;
+
+    for (e = menu->entry_list; e && no > 0; e = e->next, no--)
+      ;
+  } else {
+    for (e = menu->entry_list; e && no > 0; e = e->next, no--) {
+      /* Skip hidden entries */
+      while (e && e->hidden)
+        e = e->next;
+    }
+    while (e && e->hidden)
+      e = e->next;
+  }
 
   return e;
 }
@@ -93,10 +107,10 @@
   grub_menu_entry_t entry;
   int i;
 
-  for (i = 0, entry = menu->entry_list; i < menu->size;
+  for (i = 0, entry = menu->entry_list; entry;
        i++, entry = entry->next)
     if (entry->hotkey == hotkey)
-      return i;
+      return i | MENU_INCLUDE_HIDDEN;
 
   return -1;
 }
@@ -519,6 +533,10 @@
       grub_menu_entry_t e = menu->entry_list;
       int i;
 
+      /* Skip hidden entries */
+      while (e && e->hidden)
+	e = e->next;
+
       grub_errno = GRUB_ERR_NONE;
 
       for (i = 0; e; i++)
@@ -530,6 +548,10 @@
 	      break;
 	    }
 	  e = e->next;
+
+	  /* Skip hidden entries */
+	  while (e && e->hidden)
+	    e = e->next;
 	}
 
       if (! e)
--- a/grub-core/normal/menu_text.c
+++ b/grub-core/normal/menu_text.c
@@ -318,6 +318,10 @@
 		   e, data);
       if (e)
 	e = e->next;
+
+      /* Skip hidden entries */
+      while (e && e->hidden)
+        e = e->next;
     }
 
   grub_term_gotoxy (data->term,
--- a/include/grub/menu.h
+++ b/include/grub/menu.h
@@ -58,6 +58,8 @@
 
   int submenu;
 
+  int hidden;
+
   /* The next element.  */
   struct grub_menu_entry *next;
 };
--- a/include/grub/normal.h
+++ b/include/grub/normal.h
@@ -145,7 +145,7 @@
 			    const char *id,
 			    const char *users, const char *hotkey,
 			    const char *prefix, const char *sourcecode,
-			    int submenu);
+			    int submenu, int hidden);
 
 grub_err_t
 grub_normal_set_password (const char *user, const char *password);
