6b2dd0f
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
f4c76c0
From: Paulo Flabiano Smorigo <pfsmorigo@br.ibm.com>
f4c76c0
Date: Tue, 27 Nov 2012 17:22:07 -0200
31cddd6
Subject: [PATCH] Search for specific config file for netboot
f4c76c0
f4c76c0
This patch implements a search for a specific configuration when the config
f4c76c0
file is on a remoteserver. It uses the following order:
f4c76c0
   1) DHCP client UUID option.
f4c76c0
   2) MAC address (in lower case hexadecimal with dash separators);
f4c76c0
   3) IP (in upper case hexadecimal) or IPv6;
f4c76c0
   4) The original grub.cfg file.
f4c76c0
f4c76c0
This procedure is similar to what is used by pxelinux and yaboot:
f4c76c0
http://www.syslinux.org/wiki/index.php/PXELINUX#config
f4c76c0
f4c76c0
This should close the bugzilla:
f4c76c0
https://bugzilla.redhat.com/show_bug.cgi?id=873406
f4c76c0
---
6f1e3d5
 grub-core/net/net.c     | 118 ++++++++++++++++++++++++++++++++++++++++++++++++
6f1e3d5
 grub-core/normal/main.c |  18 ++++++--
f4c76c0
 include/grub/net.h      |   3 ++
6f1e3d5
 3 files changed, 135 insertions(+), 4 deletions(-)
f4c76c0
f4c76c0
diff --git a/grub-core/net/net.c b/grub-core/net/net.c
ec4acbb
index 10773fc3435..0769bf850d3 100644
f4c76c0
--- a/grub-core/net/net.c
f4c76c0
+++ b/grub-core/net/net.c
6f1e3d5
@@ -1735,6 +1735,124 @@ grub_net_restore_hw (void)
f4c76c0
   return GRUB_ERR_NONE;
f4c76c0
 }
f4c76c0
 
f4c76c0
+grub_err_t
f4c76c0
+grub_net_search_configfile (char *config)
f4c76c0
+{
f4c76c0
+  grub_size_t config_len;
f4c76c0
+  char *suffix;
f4c76c0
+
f4c76c0
+  auto int search_through (grub_size_t num_tries, grub_size_t slice_size);
f4c76c0
+  int search_through (grub_size_t num_tries, grub_size_t slice_size)
f4c76c0
+    {
f4c76c0
+      while (num_tries-- > 0)
f4c76c0
+        {
f4c76c0
+	  grub_dprintf ("net", "probe %s\n", config);
f4c76c0
+
f4c76c0
+          grub_file_t file;
f4c76c0
+          file = grub_file_open (config);
f4c76c0
+
f4c76c0
+          if (file)
f4c76c0
+            {
f4c76c0
+              grub_file_close (file);
f4c76c0
+              grub_dprintf ("net", "found!\n");
f4c76c0
+              return 0;
f4c76c0
+            }
f4c76c0
+          else
f4c76c0
+            {
f4c76c0
+              if (grub_errno == GRUB_ERR_IO)
f4c76c0
+                grub_errno = GRUB_ERR_NONE;
f4c76c0
+            }
f4c76c0
+
f4c76c0
+          if (grub_strlen (suffix) < slice_size)
f4c76c0
+            break;
f4c76c0
+
f4c76c0
+          config[grub_strlen (config) - slice_size] = '\0';
f4c76c0
+        }
f4c76c0
+
f4c76c0
+      return 1;
f4c76c0
+    }
f4c76c0
+
f4c76c0
+  config_len = grub_strlen (config);
f4c76c0
+  config[config_len] = '-';
f4c76c0
+  suffix = config + config_len + 1;
f4c76c0
+
f4c76c0
+  struct grub_net_network_level_interface *inf;
f4c76c0
+  FOR_NET_NETWORK_LEVEL_INTERFACES (inf)
f4c76c0
+    {
f4c76c0
+      /* By the Client UUID. */
f4c76c0
+
6f1e3d5
+      char client_uuid_var[sizeof ("net_") + grub_strlen (inf->name) +
6f1e3d5
+                           sizeof ("_clientuuid") + 1];
6f1e3d5
+      grub_snprintf (client_uuid_var, sizeof (client_uuid_var),
f4c76c0
+                     "net_%s_clientuuid", inf->name);
f4c76c0
+
f4c76c0
+      const char *client_uuid;
f4c76c0
+      client_uuid = grub_env_get (client_uuid_var);
f4c76c0
+
f4c76c0
+      if (client_uuid)
f4c76c0
+        {
f4c76c0
+          grub_strcpy (suffix, client_uuid);
f4c76c0
+          if (search_through (1, 0) == 0) return GRUB_ERR_NONE;
f4c76c0
+        }
f4c76c0
+
f4c76c0
+      /* By the MAC address. */
f4c76c0
+
f4c76c0
+      /* Add ethernet type */
f4c76c0
+      grub_strcpy (suffix, "01-");
f4c76c0
+
f4c76c0
+      grub_net_hwaddr_to_str (&inf->hwaddress, suffix + 3);
f4c76c0
+
f4c76c0
+      char *ptr;
f4c76c0
+      for (ptr = suffix; *ptr; ptr++)
f4c76c0
+        if (*ptr == ':')
f4c76c0
+          *ptr = '-';
f4c76c0
+
f4c76c0
+      if (search_through (1, 0) == 0) return GRUB_ERR_NONE;
f4c76c0
+
f4c76c0
+      /* By IP address */
f4c76c0
+
f4c76c0
+      switch ((&inf->address)->type)
f4c76c0
+        {
f4c76c0
+        case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4:
f4c76c0
+            {
f4c76c0
+              grub_uint32_t n = grub_be_to_cpu32 ((&inf->address)->ipv4);
f4c76c0
+              grub_snprintf (suffix, GRUB_NET_MAX_STR_ADDR_LEN, "%02X%02X%02X%02X", \
f4c76c0
+                             ((n >> 24) & 0xff), ((n >> 16) & 0xff), \
f4c76c0
+                             ((n >> 8) & 0xff), ((n >> 0) & 0xff));
f4c76c0
+
f4c76c0
+              if (search_through (8, 1) == 0) return GRUB_ERR_NONE;
f4c76c0
+              break;
f4c76c0
+            }
f4c76c0
+        case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6:
f4c76c0
+            {
f4c76c0
+              char buf[GRUB_NET_MAX_STR_ADDR_LEN];
f4c76c0
+              struct grub_net_network_level_address base;
f4c76c0
+              base.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
f4c76c0
+              grub_memcpy (&base.ipv6, ((&inf->address)->ipv6), 16);
f4c76c0
+              grub_net_addr_to_str (&base, buf);
f4c76c0
+
f4c76c0
+              for (ptr = buf; *ptr; ptr++)
f4c76c0
+                if (*ptr == ':')
f4c76c0
+                  *ptr = '-';
f4c76c0
+
f4c76c0
+              grub_snprintf (suffix, GRUB_NET_MAX_STR_ADDR_LEN, "%s", buf);
f4c76c0
+              if (search_through (1, 0) == 0) return GRUB_ERR_NONE;
f4c76c0
+              break;
f4c76c0
+            }
f4c76c0
+        case GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV:
f4c76c0
+          return grub_error (GRUB_ERR_BUG, "shouldn't reach here");
f4c76c0
+        default:
f4c76c0
+          return grub_error (GRUB_ERR_BUG,
f4c76c0
+                             "unsupported address type %d", (&inf->address)->type);
f4c76c0
+        }
f4c76c0
+    }
f4c76c0
+
f4c76c0
+  /* Remove the remaining minus sign at the end. */
f4c76c0
+  config[config_len] = '\0';
f4c76c0
+
f4c76c0
+  return GRUB_ERR_NONE;
f4c76c0
+}
f4c76c0
+
f4c76c0
 static struct grub_preboot *fini_hnd;
f4c76c0
 
f4c76c0
 static grub_command_t cmd_addaddr, cmd_deladdr, cmd_addroute, cmd_delroute;
f4c76c0
diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
ec4acbb
index 759c475c4d9..b2654ef62e8 100644
f4c76c0
--- a/grub-core/normal/main.c
f4c76c0
+++ b/grub-core/normal/main.c
f4c76c0
@@ -33,6 +33,7 @@
f4c76c0
 #include <grub/charset.h>
f4c76c0
 #include <grub/script_sh.h>
f4c76c0
 #include <grub/bufio.h>
f4c76c0
+#include <grub/net.h>
f4c76c0
 #ifdef GRUB_MACHINE_IEEE1275
f4c76c0
 #include <grub/ieee1275/ieee1275.h>
f4c76c0
 #endif
f4c76c0
@@ -365,10 +366,19 @@ grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)),
f4c76c0
 
f4c76c0
       prefix = grub_env_get ("prefix");
f4c76c0
       if (prefix)
f4c76c0
-	{
f4c76c0
-	  config = grub_xasprintf ("%s/grub.cfg", prefix);
f4c76c0
-	  if (! config)
f4c76c0
-	    goto quit;
f4c76c0
+        {
f4c76c0
+          grub_size_t config_len;
f4c76c0
+          config_len = grub_strlen (prefix) +
f4c76c0
+                      sizeof ("/grub.cfg-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX");
f4c76c0
+          config = grub_malloc (config_len);
f4c76c0
+
f4c76c0
+          if (! config)
f4c76c0
+            goto quit;
f4c76c0
+
f4c76c0
+          grub_snprintf (config, config_len, "%s/grub.cfg", prefix);
f4c76c0
+
f4c76c0
+          if (grub_strncmp (prefix + 1, "tftp", sizeof ("tftp") - 1) == 0)
f4c76c0
+            grub_net_search_configfile (config);
f4c76c0
 
f4c76c0
 	  grub_enter_normal_mode (config);
f4c76c0
 	  grub_free (config);
f4c76c0
diff --git a/include/grub/net.h b/include/grub/net.h
ec4acbb
index e266bae23f4..50d62ab0c8c 100644
f4c76c0
--- a/include/grub/net.h
f4c76c0
+++ b/include/grub/net.h
bc092b9
@@ -566,4 +566,7 @@ extern char *grub_net_default_server;
f4c76c0
 
f4c76c0
 #define VLANTAG_IDENTIFIER 0x8100
f4c76c0
 
f4c76c0
+grub_err_t
f4c76c0
+grub_net_search_configfile (char *config);
f4c76c0
+
f4c76c0
 #endif /* ! GRUB_NET_HEADER */