ea2871d
From b9f953dc0a2297fab25b968dce4f7320d022c3e1 Mon Sep 17 00:00:00 2001
f4c76c0
From: Robert Marshall <rmarshall@redhat.com>
f4c76c0
Date: Mon, 16 Mar 2015 14:14:19 -0400
f4c76c0
Subject: [PATCH 69/74] Use Distribution Package Sort for grub2-mkconfig
f4c76c0
 (#1124074)
f4c76c0
f4c76c0
Users reported that newly installed kernels on their systems installed
f4c76c0
with grub-mkconfig would not appear on the grub boot list in order
f4c76c0
starting with the most recent. Added an option for rpm-based systems to
f4c76c0
use the rpm-sort library to sort kernels instead.
f4c76c0
f4c76c0
Resolves rhbz#1124074
f4c76c0
---
f4c76c0
 .gitignore                |   1 +
f4c76c0
 Makefile.util.def         |  16 +++
f4c76c0
 configure.ac              |  29 +++++
ea2871d
 util/grub-mkconfig_lib.in |  11 +-
f4c76c0
 util/grub-rpm-sort.8      |  12 ++
f4c76c0
 util/grub-rpm-sort.c      | 281 ++++++++++++++++++++++++++++++++++++++++++++++
ea2871d
 6 files changed, 349 insertions(+), 1 deletion(-)
f4c76c0
 create mode 100644 util/grub-rpm-sort.8
f4c76c0
 create mode 100644 util/grub-rpm-sort.c
f4c76c0
f4c76c0
diff --git a/.gitignore b/.gitignore
f4c76c0
index 06c9f3a..7697877 100644
f4c76c0
--- a/.gitignore
f4c76c0
+++ b/.gitignore
f4c76c0
@@ -124,6 +124,7 @@ grub-ofpathname
f4c76c0
 grub-probe
f4c76c0
 grub-reboot
f4c76c0
 grub-render-label
f4c76c0
+grub-rpm-sort
f4c76c0
 grub-script-check
f4c76c0
 grub-set-default
f4c76c0
 grub-shell
f4c76c0
diff --git a/Makefile.util.def b/Makefile.util.def
f4c76c0
index 591c5e5..f28d73d 100644
f4c76c0
--- a/Makefile.util.def
f4c76c0
+++ b/Makefile.util.def
f4c76c0
@@ -674,6 +674,22 @@ program = {
f4c76c0
   ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
f4c76c0
 };
f4c76c0
 
f4c76c0
+program = {
f4c76c0
+  name = grub-rpm-sort;
f4c76c0
+  mansection = 8;
f4c76c0
+  installdir = sbin;
f4c76c0
+
f4c76c0
+  common = grub-core/kern/emu/misc.c;
f4c76c0
+  common = grub-core/kern/emu/argp_common.c;
f4c76c0
+  common = grub-core/osdep/init.c;
f4c76c0
+  common = util/misc.c;
f4c76c0
+  common = util/grub-rpm-sort.c;
f4c76c0
+
f4c76c0
+  ldadd = grub-core/gnulib/libgnu.a;
f4c76c0
+  ldadd = libgrubkern.a;
f4c76c0
+  ldadd = '$(LIBDEVMAPPER) $(LIBRPM)';
f4c76c0
+};
f4c76c0
+
f4c76c0
 script = {
f4c76c0
   name = grub-mkconfig;
f4c76c0
   common = util/grub-mkconfig.in;
f4c76c0
diff --git a/configure.ac b/configure.ac
ea2871d
index 357c94e..4f26f6c 100644
f4c76c0
--- a/configure.ac
f4c76c0
+++ b/configure.ac
f4c76c0
@@ -65,6 +65,7 @@ grub_TRANSFORM([grub-mkrelpath])
f4c76c0
 grub_TRANSFORM([grub-mkrescue])
f4c76c0
 grub_TRANSFORM([grub-probe])
f4c76c0
 grub_TRANSFORM([grub-reboot])
f4c76c0
+grub_TRANSFORM([grub-rpm-sort])
f4c76c0
 grub_TRANSFORM([grub-script-check])
f4c76c0
 grub_TRANSFORM([grub-set-default])
f4c76c0
 grub_TRANSFORM([grub-sparc64-setup])
f4c76c0
@@ -88,6 +89,7 @@ grub_TRANSFORM([grub-mkrescue.1])
f4c76c0
 grub_TRANSFORM([grub-mkstandalone.3])
f4c76c0
 grub_TRANSFORM([grub-ofpathname.3])
f4c76c0
 grub_TRANSFORM([grub-probe.3])
f4c76c0
+grub_TRANSFORM([grub-rpm-sort.8])
f4c76c0
 grub_TRANSFORM([grub-reboot.3])
f4c76c0
 grub_TRANSFORM([grub-render-label.3])
f4c76c0
 grub_TRANSFORM([grub-script-check.3])
f4c76c0
@@ -1630,6 +1632,33 @@ fi
f4c76c0
 
f4c76c0
 AC_SUBST([LIBDEVMAPPER])
f4c76c0
 
f4c76c0
+AC_ARG_ENABLE([rpm-sort],
f4c76c0
+              [AS_HELP_STRING([--enable-rpm-sort],
f4c76c0
+                              [enable native rpm sorting of kernels in grub (default=guessed)])])
ea2871d
+if test x"$enable_rpm_sort" = xno ; then
f4c76c0
+  rpm_sort_excuse="explicitly disabled"
f4c76c0
+fi
f4c76c0
+
f4c76c0
+if test x"$rpm_sort_excuse" = x ; then
f4c76c0
+  # Check for rpmlib header.
f4c76c0
+  AC_CHECK_HEADER([rpm/rpmlib.h], [],
f4c76c0
+               [rpm_sort_excuse="need rpm/rpmlib header"])
f4c76c0
+fi
f4c76c0
+
f4c76c0
+if test x"$rpm_sort_excuse" = x ; then
f4c76c0
+  # Check for rpm library.
f4c76c0
+  AC_CHECK_LIB([rpm], [rpmvercmp], [],
f4c76c0
+               [rpm_sort_excuse="rpmlib missing rpmvercmp"])
f4c76c0
+fi
f4c76c0
+
f4c76c0
+if test x"$rpm_sort_excuse" = x ; then
f4c76c0
+   LIBRPM="-lrpm";
f4c76c0
+   AC_DEFINE([HAVE_RPM], [1],
f4c76c0
+             [Define to 1 if you have the rpm library.])
f4c76c0
+fi
f4c76c0
+
f4c76c0
+AC_SUBST([LIBRPM])
f4c76c0
+
f4c76c0
 LIBGEOM=
f4c76c0
 if test x$host_kernel = xkfreebsd; then
f4c76c0
   AC_CHECK_LIB([geom], [geom_gettree], [],
f4c76c0
diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in
ea2871d
index 10fabee..9c410ea 100644
f4c76c0
--- a/util/grub-mkconfig_lib.in
f4c76c0
+++ b/util/grub-mkconfig_lib.in
ea2871d
@@ -33,6 +33,9 @@ fi
ea2871d
 if test "x$grub_mkrelpath" = x; then
ea2871d
   grub_mkrelpath="${bindir}/@grub_mkrelpath@"
ea2871d
 fi
ea2871d
+if test "x$grub_rpm_sort" = x; then
ea2871d
+  grub_rpm_sort="${sbindir}/@grub_rpm_sort@"
ea2871d
+fi
ea2871d
 
ea2871d
 if which gettext >/dev/null 2>/dev/null; then
ea2871d
   :
ea2871d
@@ -213,6 +216,12 @@ version_sort ()
f4c76c0
    esac
f4c76c0
 }
f4c76c0
 
ea2871d
+if [ "x$grub_rpm_sort" != x -a -x "$grub_rpm_sort" ]; then
ea2871d
+  kernel_sort="$grub_rpm_sort"
f4c76c0
+else
ea2871d
+  kernel_sort=version_sort
f4c76c0
+fi
f4c76c0
+
f4c76c0
 version_test_numeric ()
f4c76c0
 {
f4c76c0
   version_test_numeric_a="$1"
ea2871d
@@ -229,7 +238,7 @@ version_test_numeric ()
f4c76c0
     version_test_numeric_a="$version_test_numeric_b"
f4c76c0
     version_test_numeric_b="$version_test_numeric_c"
f4c76c0
   fi
f4c76c0
-  if (echo "$version_test_numeric_a" ; echo "$version_test_numeric_b") | version_sort | head -n 1 | grep -qx "$version_test_numeric_b" ; then
f4c76c0
+  if (echo "$version_test_numeric_a" ; echo "$version_test_numeric_b") | "$kernel_sort" | head -n 1 | grep -qx "$version_test_numeric_b" ; then
f4c76c0
     return 0
f4c76c0
   else
f4c76c0
     return 1
f4c76c0
diff --git a/util/grub-rpm-sort.8 b/util/grub-rpm-sort.8
f4c76c0
new file mode 100644
f4c76c0
index 0000000..8ce2148
f4c76c0
--- /dev/null
f4c76c0
+++ b/util/grub-rpm-sort.8
f4c76c0
@@ -0,0 +1,12 @@
f4c76c0
+.TH GRUB-RPM-SORT 8 "Wed Feb 26 2014"
f4c76c0
+.SH NAME
f4c76c0
+\fBgrub-rpm-sort\fR \(em Sort input according to RPM version compare.
f4c76c0
+
f4c76c0
+.SH SYNOPSIS
f4c76c0
+\fBgrub-rpm-sort\fR [OPTIONS].
f4c76c0
+
f4c76c0
+.SH DESCRIPTION
f4c76c0
+You should not normally run this program directly.  Use grub-mkconfig instead.
f4c76c0
+
f4c76c0
+.SH SEE ALSO
f4c76c0
+.BR "info grub"
f4c76c0
diff --git a/util/grub-rpm-sort.c b/util/grub-rpm-sort.c
f4c76c0
new file mode 100644
ea2871d
index 0000000..f33bd1e
f4c76c0
--- /dev/null
f4c76c0
+++ b/util/grub-rpm-sort.c
f4c76c0
@@ -0,0 +1,281 @@
f4c76c0
+#include <config.h>
f4c76c0
+#include <grub/types.h>
f4c76c0
+#include <grub/util/misc.h>
f4c76c0
+#include <stdio.h>
f4c76c0
+#include <stdlib.h>
f4c76c0
+#include <unistd.h>
f4c76c0
+#include <errno.h>
f4c76c0
+#include <assert.h>
f4c76c0
+#include <argp.h>
f4c76c0
+#include <rpm/rpmlib.h>
f4c76c0
+
f4c76c0
+static size_t
f4c76c0
+read_file (const char *input, char **ret)
f4c76c0
+{
f4c76c0
+  FILE *in;
f4c76c0
+  size_t s;
f4c76c0
+  size_t sz = 2048;
f4c76c0
+  size_t offset = 0;
f4c76c0
+  char *text;
f4c76c0
+
f4c76c0
+  if (!strcmp(input, "-"))
f4c76c0
+    in = stdin;
f4c76c0
+  else
f4c76c0
+    in = grub_util_fopen(input, "r");
f4c76c0
+
f4c76c0
+  text = xmalloc (sz);
f4c76c0
+
f4c76c0
+  if (!in)
f4c76c0
+    grub_util_error (_("cannot open `%s': %s"), input, strerror (errno));
f4c76c0
+
f4c76c0
+  while ((s = fread (text + offset, 1, sz - offset, in)) != 0)
f4c76c0
+    {
f4c76c0
+      offset += s;
f4c76c0
+      if (sz - offset == 0)
f4c76c0
+	{
f4c76c0
+	  sz += 2048;
f4c76c0
+	  text = xrealloc (text, sz);
f4c76c0
+	}
f4c76c0
+    }
f4c76c0
+
f4c76c0
+  text[offset] = '\0';
f4c76c0
+  *ret = text;
f4c76c0
+
f4c76c0
+  if (in != stdin)
f4c76c0
+    fclose(in);
f4c76c0
+
f4c76c0
+  return offset + 1;
f4c76c0
+}
f4c76c0
+
f4c76c0
+/* returns name/version/release */
f4c76c0
+/* NULL string pointer returned if nothing found */
f4c76c0
+static void
f4c76c0
+split_package_string (char *package_string, char **name,
f4c76c0
+                     char **version, char **release)
f4c76c0
+{
f4c76c0
+  char *package_version, *package_release;
f4c76c0
+
f4c76c0
+  /* Release */
f4c76c0
+  package_release = strrchr (package_string, '-');
f4c76c0
+
f4c76c0
+  if (package_release != NULL)
f4c76c0
+      *package_release++ = '\0';
f4c76c0
+
f4c76c0
+  *release = package_release;
f4c76c0
+
f4c76c0
+  /* Version */
f4c76c0
+  package_version = strrchr(package_string, '-');
f4c76c0
+
f4c76c0
+  if (package_version != NULL)
f4c76c0
+      *package_version++ = '\0';
f4c76c0
+
f4c76c0
+  *version = package_version;
f4c76c0
+  /* Name */
f4c76c0
+  *name = package_string;
f4c76c0
+
f4c76c0
+  /* Bubble up non-null values from release to name */
f4c76c0
+  if (*name == NULL)
f4c76c0
+    {
f4c76c0
+      *name = (*version == NULL ? *release : *version);
f4c76c0
+      *version = *release;
f4c76c0
+      *release = NULL;
f4c76c0
+    }
f4c76c0
+  if (*version == NULL)
f4c76c0
+    {
f4c76c0
+      *version = *release;
f4c76c0
+      *release = NULL;
f4c76c0
+    }
f4c76c0
+}
f4c76c0
+
f4c76c0
+/*
f4c76c0
+ * package name-version-release comparator for qsort
f4c76c0
+ * expects p, q which are pointers to character strings (char *)
f4c76c0
+ * which will not be altered in this function
f4c76c0
+ */
f4c76c0
+static int
f4c76c0
+package_version_compare (const void *p, const void *q)
f4c76c0
+{
f4c76c0
+  char *local_p, *local_q;
f4c76c0
+  char *lhs_name, *lhs_version, *lhs_release;
f4c76c0
+  char *rhs_name, *rhs_version, *rhs_release;
f4c76c0
+  int vercmpflag = 0;
f4c76c0
+
f4c76c0
+  local_p = alloca (strlen (*(char * const *)p) + 1);
f4c76c0
+  local_q = alloca (strlen (*(char * const *)q) + 1);
f4c76c0
+
f4c76c0
+  /* make sure these allocated */
f4c76c0
+  assert (local_p);
f4c76c0
+  assert (local_q);
f4c76c0
+
f4c76c0
+  strcpy (local_p, *(char * const *)p);
f4c76c0
+  strcpy (local_q, *(char * const *)q);
f4c76c0
+
f4c76c0
+  split_package_string (local_p, &lhs_name, &lhs_version, &lhs_release);
f4c76c0
+  split_package_string (local_q, &rhs_name, &rhs_version, &rhs_release);
f4c76c0
+
f4c76c0
+  /* Check Name and return if unequal */
f4c76c0
+  vercmpflag = rpmvercmp ((lhs_name == NULL ? "" : lhs_name),
f4c76c0
+                          (rhs_name == NULL ? "" : rhs_name));
f4c76c0
+  if (vercmpflag != 0)
ea2871d
+    return vercmpflag;
f4c76c0
+
f4c76c0
+  /* Check version and return if unequal */
f4c76c0
+  vercmpflag = rpmvercmp ((lhs_version == NULL ? "" : lhs_version),
f4c76c0
+                          (rhs_version == NULL ? "" : rhs_version));
f4c76c0
+  if (vercmpflag != 0)
ea2871d
+    return vercmpflag;
f4c76c0
+
f4c76c0
+  /* Check release and return the version compare value */
f4c76c0
+  vercmpflag = rpmvercmp ((lhs_release == NULL ? "" : lhs_release),
f4c76c0
+                          (rhs_release == NULL ? "" : rhs_release));
f4c76c0
+
ea2871d
+  return vercmpflag;
f4c76c0
+}
f4c76c0
+
f4c76c0
+static void
f4c76c0
+add_input (const char *filename, char ***package_names, size_t *n_package_names)
f4c76c0
+{
f4c76c0
+  char *orig_input_buffer = NULL;
f4c76c0
+  char *input_buffer;
f4c76c0
+  char *position_of_newline;
f4c76c0
+  char **names = *package_names;
f4c76c0
+  char **new_names = NULL;
f4c76c0
+  size_t n_names = *n_package_names;
f4c76c0
+
f4c76c0
+  if (!*package_names)
f4c76c0
+    new_names = names = xmalloc (sizeof (char *) * 2);
f4c76c0
+
f4c76c0
+  if (read_file (filename, &orig_input_buffer) < 2)
f4c76c0
+    {
f4c76c0
+      if (new_names)
f4c76c0
+	free (new_names);
f4c76c0
+      if (orig_input_buffer)
f4c76c0
+	free (orig_input_buffer);
f4c76c0
+      return;
f4c76c0
+    }
f4c76c0
+
f4c76c0
+  input_buffer = orig_input_buffer;
f4c76c0
+  while (input_buffer && *input_buffer &&
f4c76c0
+	 (position_of_newline = strchrnul (input_buffer, '\n')))
f4c76c0
+    {
f4c76c0
+      size_t sz = position_of_newline - input_buffer;
f4c76c0
+      char *new;
f4c76c0
+
f4c76c0
+      if (sz == 0)
f4c76c0
+	{
f4c76c0
+	  input_buffer = position_of_newline + 1;
f4c76c0
+	  continue;
f4c76c0
+	}
f4c76c0
+
f4c76c0
+      new = xmalloc (sz+1);
f4c76c0
+      strncpy (new, input_buffer, sz);
f4c76c0
+      new[sz] = '\0';
f4c76c0
+
f4c76c0
+      names = xrealloc (names, sizeof (char *) * (n_names + 1));
f4c76c0
+      names[n_names] = new;
f4c76c0
+      n_names++;
f4c76c0
+
f4c76c0
+      /* move buffer ahead to next line */
f4c76c0
+      input_buffer = position_of_newline + 1;
f4c76c0
+      if (*position_of_newline == '\0')
f4c76c0
+	input_buffer = NULL;
f4c76c0
+    }
f4c76c0
+
f4c76c0
+  free (orig_input_buffer);
f4c76c0
+
f4c76c0
+  *package_names = names;
f4c76c0
+  *n_package_names = n_names;
f4c76c0
+}
f4c76c0
+
f4c76c0
+static char *
f4c76c0
+help_filter (int key, const char *text, void *input __attribute__ ((unused)))
f4c76c0
+{
f4c76c0
+  return (char *)text;
f4c76c0
+}
f4c76c0
+
f4c76c0
+static struct argp_option options[] = {
f4c76c0
+  { 0, }
f4c76c0
+};
f4c76c0
+
f4c76c0
+struct arguments
f4c76c0
+{
f4c76c0
+  size_t ninputs;
f4c76c0
+  size_t input_max;
f4c76c0
+  char **inputs;
f4c76c0
+};
f4c76c0
+
f4c76c0
+static error_t
f4c76c0
+argp_parser (int key, char *arg, struct argp_state *state)
f4c76c0
+{
f4c76c0
+  struct arguments *arguments = state->input;
f4c76c0
+  switch (key)
f4c76c0
+    {
f4c76c0
+    case ARGP_KEY_ARG:
f4c76c0
+      assert (arguments->ninputs < arguments->input_max);
f4c76c0
+      arguments->inputs[arguments->ninputs++] = xstrdup (arg);
f4c76c0
+      break;
f4c76c0
+    default:
f4c76c0
+      return ARGP_ERR_UNKNOWN;
f4c76c0
+    }
f4c76c0
+  return 0;
f4c76c0
+}
f4c76c0
+
f4c76c0
+static struct argp argp = {
f4c76c0
+  options, argp_parser, N_("[INPUT_FILES]"),
f4c76c0
+  N_("Sort a list of strings in RPM version sort order."),
f4c76c0
+  NULL, help_filter, NULL
f4c76c0
+};
f4c76c0
+
f4c76c0
+int
f4c76c0
+main (int argc, char *argv[])
f4c76c0
+{
f4c76c0
+  struct arguments arguments;
f4c76c0
+  char **package_names = NULL;
f4c76c0
+  size_t n_package_names = 0;
f4c76c0
+  int i;
f4c76c0
+
f4c76c0
+  grub_util_host_init (&argc, &argv);
f4c76c0
+
f4c76c0
+  memset (&arguments, 0, sizeof (struct arguments));
f4c76c0
+  arguments.input_max = argc+1;
f4c76c0
+  arguments.inputs = xmalloc ((arguments.input_max + 1)
f4c76c0
+			      * sizeof (arguments.inputs[0]));
f4c76c0
+  memset (arguments.inputs, 0, (arguments.input_max + 1)
f4c76c0
+	  * sizeof (arguments.inputs[0]));
f4c76c0
+
f4c76c0
+  /* Parse our arguments */
f4c76c0
+  if (argp_parse (&argp, argc, argv, 0, 0, &arguments) != 0)
f4c76c0
+    grub_util_error ("%s", _("Error in parsing command line arguments\n"));
f4c76c0
+
f4c76c0
+  /* If there's no inputs in argv, add one for stdin */
f4c76c0
+  if (!arguments.ninputs)
f4c76c0
+    {
f4c76c0
+      arguments.ninputs = 1;
f4c76c0
+      arguments.inputs[0] = xmalloc (2);
f4c76c0
+      strcpy(arguments.inputs[0], "-");
f4c76c0
+    }
f4c76c0
+
f4c76c0
+  for (i = 0; i < arguments.ninputs; i++)
f4c76c0
+    add_input(arguments.inputs[i], &package_names, &n_package_names);
f4c76c0
+
f4c76c0
+  if (package_names == NULL || n_package_names < 1)
f4c76c0
+    grub_util_error ("%s", _("Invalid input\n"));
f4c76c0
+
f4c76c0
+  qsort (package_names, n_package_names, sizeof (char *),
f4c76c0
+	 package_version_compare);
f4c76c0
+
f4c76c0
+  /* send sorted list to stdout */
f4c76c0
+  for (i = 0; i < n_package_names; i++)
f4c76c0
+    {
f4c76c0
+      fprintf (stdout, "%s\n", package_names[i]);
f4c76c0
+      free (package_names[i]);
f4c76c0
+    }
f4c76c0
+
f4c76c0
+  free (package_names);
f4c76c0
+  for (i = 0; i < arguments.ninputs; i++)
f4c76c0
+    free (arguments.inputs[i]);
f4c76c0
+
f4c76c0
+  free (arguments.inputs);
f4c76c0
+
f4c76c0
+  return 0;
f4c76c0
+}
f4c76c0
-- 
f4c76c0
2.4.3
f4c76c0