nalika / rpms / grub2

Forked from rpms/grub2 2 years ago
Clone

Blame SOURCES/0190-Add-grub-set-bootflag-utility.patch

d9d99f
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
d9d99f
From: Hans de Goede <hdegoede@redhat.com>
d9d99f
Date: Tue, 12 Jun 2018 13:25:16 +0200
d9d99f
Subject: [PATCH] Add grub-set-bootflag utility
d9d99f
d9d99f
This commit adds a new grub-set-bootflag utility, which can be used
d9d99f
to set known bootflags in the grubenv: boot_success or menu_show_once.
d9d99f
d9d99f
grub-set-bootflag is different from grub-editenv in 2 ways:
d9d99f
b71686
1) It is intended to be executed by regular users so must be installed
b71686
as suid root. As such it is written to not use any existing grubenv
b71686
related code for easy auditing.
b71686
b71686
It can't be executed through pkexec because we want to call it under gdm
b71686
and pkexec does not work under gdm due the gdm user having /sbin/nologin
b71686
as shell.
d9d99f
d9d99f
2) Since it can be executed by regular users it only allows setting
d9d99f
(assigning a value of 1 to) bootflags which it knows about. Currently
d9d99f
those are just boot_success and menu_show_once.
d9d99f
b71686
This commit also adds a couple of example systemd and files which show
b71686
how this can be used to set boot_success from a user-session:
d9d99f
d9d99f
docs/grub-boot-success.service
d9d99f
docs/grub-boot-success.timer
d9d99f
d9d99f
The 2 grub-boot-success.systemd files should be placed in /lib/systemd/user
d9d99f
and a symlink to grub-boot-success.timer should be added to
d9d99f
/lib/systemd/user/timers.target.wants.
d9d99f
d9d99f
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
b71686
[makhomed: grub-boot-success.timer: Only run if not in a container]
b71686
Signed-off-by: Gena Makhomed <makhomed@gmail.com>
b71686
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
b71686
(cherry-picked from commit 555625062cbb10388bb706620e2d8839644fc015)
d9d99f
---
d9d99f
 Makefile.util.def              |   7 ++
b71686
 util/grub-set-bootflag.c       | 160 +++++++++++++++++++++++++++++++++++++++++
d9d99f
 conf/Makefile.extra-dist       |   3 +
d9d99f
 docs/grub-boot-success.service |   6 ++
b71686
 docs/grub-boot-success.timer   |   7 ++
d9d99f
 util/grub-set-bootflag.1       |  20 ++++++
b71686
 6 files changed, 203 insertions(+)
d9d99f
 create mode 100644 util/grub-set-bootflag.c
d9d99f
 create mode 100644 docs/grub-boot-success.service
d9d99f
 create mode 100644 docs/grub-boot-success.timer
d9d99f
 create mode 100644 util/grub-set-bootflag.1
d9d99f
d9d99f
diff --git a/Makefile.util.def b/Makefile.util.def
b71686
index 5a8c390a1..5da553932 100644
d9d99f
--- a/Makefile.util.def
d9d99f
+++ b/Makefile.util.def
d9d99f
@@ -1435,3 +1435,10 @@ program = {
d9d99f
   ldadd = grub-core/gnulib/libgnu.a;
d9d99f
   ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
d9d99f
 };
d9d99f
+
d9d99f
+program = {
d9d99f
+  name = grub-set-bootflag;
d9d99f
+  installdir = sbin;
d9d99f
+  mansection = 1;
d9d99f
+  common = util/grub-set-bootflag.c;
d9d99f
+};
d9d99f
diff --git a/util/grub-set-bootflag.c b/util/grub-set-bootflag.c
d9d99f
new file mode 100644
b71686
index 000000000..bb198f023
d9d99f
--- /dev/null
d9d99f
+++ b/util/grub-set-bootflag.c
b71686
@@ -0,0 +1,160 @@
d9d99f
+/* grub-set-bootflag.c - tool to set boot-flags in the grubenv. */
d9d99f
+/*
d9d99f
+ *  GRUB  --  GRand Unified Bootloader
d9d99f
+ *  Copyright (C) 2018 Free Software Foundation, Inc.
d9d99f
+ *
d9d99f
+ *  GRUB is free software: you can redistribute it and/or modify
d9d99f
+ *  it under the terms of the GNU General Public License as published by
d9d99f
+ *  the Free Software Foundation, either version 3 of the License, or
d9d99f
+ *  (at your option) any later version.
d9d99f
+ *
d9d99f
+ *  GRUB is distributed in the hope that it will be useful,
d9d99f
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
d9d99f
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
d9d99f
+ *  GNU General Public License for more details.
d9d99f
+ *
d9d99f
+ *  You should have received a copy of the GNU General Public License
d9d99f
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
d9d99f
+ */
d9d99f
+
d9d99f
+/*
d9d99f
+ * NOTE this gets run by users as root (through pkexec), so this does not
d9d99f
+ * use any grub library / util functions to allow for easy auditing.
d9d99f
+ * The grub headers are only included to get certain defines.
d9d99f
+ */
d9d99f
+
d9d99f
+#include <config-util.h>     /* For *_DIR_NAME defines */
d9d99f
+#include <grub/types.h>
d9d99f
+#include <grub/lib/envblk.h> /* For GRUB_ENVBLK_DEFCFG define */
b71686
+#include <errno.h>
d9d99f
+#include <stdio.h>
d9d99f
+#include <string.h>
d9d99f
+#include <unistd.h>
d9d99f
+
d9d99f
+#define GRUBENV "/" GRUB_BOOT_DIR_NAME "/" GRUB_DIR_NAME "/" GRUB_ENVBLK_DEFCFG
d9d99f
+#define GRUBENV_SIZE 1024
d9d99f
+
d9d99f
+const char *bootflags[] = {
d9d99f
+  "boot_success",
d9d99f
+  "menu_show_once",
d9d99f
+  NULL
d9d99f
+};
d9d99f
+
d9d99f
+static void usage(void)
d9d99f
+{
d9d99f
+  int i;
d9d99f
+
d9d99f
+  fprintf (stderr, "Usage: 'grub-set-bootflag <bootflag>', where <bootflag> is one of:\n");
d9d99f
+  for (i = 0; bootflags[i]; i++)
d9d99f
+    fprintf (stderr, "  %s\n", bootflags[i]);
d9d99f
+}
d9d99f
+
d9d99f
+int main(int argc, char *argv[])
d9d99f
+{
d9d99f
+  /* NOTE buf must be at least the longest bootflag length + 4 bytes */
d9d99f
+  char env[GRUBENV_SIZE + 1], buf[64], *s;
d9d99f
+  const char *bootflag;
d9d99f
+  int i, len, ret;
d9d99f
+  FILE *f;
d9d99f
+
d9d99f
+  if (argc != 2)
d9d99f
+    {
d9d99f
+      usage();
d9d99f
+      return 1;
d9d99f
+    }
d9d99f
+
d9d99f
+  for (i = 0; bootflags[i]; i++)
d9d99f
+    if (!strcmp (argv[1], bootflags[i]))
d9d99f
+      break;
d9d99f
+  if (!bootflags[i])
d9d99f
+    {
d9d99f
+      fprintf (stderr, "Invalid bootflag: '%s'\n", argv[1]);
d9d99f
+      usage();
d9d99f
+      return 1;
d9d99f
+    }
d9d99f
+
d9d99f
+  bootflag = bootflags[i];
d9d99f
+  len = strlen (bootflag);
d9d99f
+
d9d99f
+  f = fopen (GRUBENV, "r");
d9d99f
+  if (!f)
d9d99f
+    {
d9d99f
+      perror ("Error opening " GRUBENV " for reading");
d9d99f
+      return 1;     
d9d99f
+    }
d9d99f
+
d9d99f
+  ret = fread (env, 1, GRUBENV_SIZE, f);
d9d99f
+  fclose (f);
d9d99f
+  if (ret != GRUBENV_SIZE)
d9d99f
+    {
b71686
+      errno = EINVAL;
d9d99f
+      perror ("Error reading from " GRUBENV);
d9d99f
+      return 1;     
d9d99f
+    }
d9d99f
+
d9d99f
+  /* 0 terminate env */
d9d99f
+  env[GRUBENV_SIZE] = 0;
d9d99f
+
d9d99f
+  if (strncmp (env, GRUB_ENVBLK_SIGNATURE, strlen (GRUB_ENVBLK_SIGNATURE)))
d9d99f
+    {
d9d99f
+      fprintf (stderr, "Error invalid environment block\n");
d9d99f
+      return 1;
d9d99f
+    }
d9d99f
+
d9d99f
+  /* Find a pre-existing definition of the bootflag */
d9d99f
+  s = strstr (env, bootflag);
d9d99f
+  while (s && s[len] != '=')
d9d99f
+    s = strstr (s + len, bootflag);
d9d99f
+
d9d99f
+  if (s && ((s[len + 1] != '0' && s[len + 1] != '1') || s[len + 2] != '\n'))
d9d99f
+    {
d9d99f
+      fprintf (stderr, "Pre-existing bootflag '%s' has unexpected value\n", bootflag);
d9d99f
+      return 1;     
d9d99f
+    }
d9d99f
+
d9d99f
+  /* No pre-existing bootflag? -> find free space */
d9d99f
+  if (!s)
d9d99f
+    {
d9d99f
+      for (i = 0; i < (len + 3); i++)
d9d99f
+        buf[i] = '#';
d9d99f
+      buf[i] = 0;
d9d99f
+      s = strstr (env, buf);
d9d99f
+    }
d9d99f
+
d9d99f
+  if (!s)
d9d99f
+    {
d9d99f
+      fprintf (stderr, "No space in grubenv to store bootflag '%s'\n", bootflag);
d9d99f
+      return 1;     
d9d99f
+    }
d9d99f
+
d9d99f
+  /* The grubenv is not 0 terminated, so memcpy the name + '=' , '1', '\n' */
d9d99f
+  snprintf(buf, sizeof(buf), "%s=1\n", bootflag);
d9d99f
+  memcpy(s, buf, len + 3);
d9d99f
+
d9d99f
+  /* "r+", don't truncate so that the diskspace stays reserved */
d9d99f
+  f = fopen (GRUBENV, "r+");
d9d99f
+  if (!f)
d9d99f
+    {
d9d99f
+      perror ("Error opening " GRUBENV " for writing");
d9d99f
+      return 1;     
d9d99f
+    }
d9d99f
+
d9d99f
+  ret = fwrite (env, 1, GRUBENV_SIZE, f);
d9d99f
+  if (ret != GRUBENV_SIZE)
d9d99f
+    {
d9d99f
+      perror ("Error writing to " GRUBENV);
d9d99f
+      return 1;     
d9d99f
+    }
d9d99f
+
d9d99f
+  ret = fflush (f);
d9d99f
+  if (ret)
d9d99f
+    {
d9d99f
+      perror ("Error flushing " GRUBENV);
d9d99f
+      return 1;     
d9d99f
+    }
d9d99f
+
d9d99f
+  fsync (fileno (f));
d9d99f
+  fclose (f);
d9d99f
+
d9d99f
+  return 0;
d9d99f
+}
d9d99f
diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist
b71686
index 39eb94bde..5946ec24a 100644
d9d99f
--- a/conf/Makefile.extra-dist
d9d99f
+++ b/conf/Makefile.extra-dist
d9d99f
@@ -14,6 +14,9 @@ EXTRA_DIST += util/import_unicode.py
d9d99f
 EXTRA_DIST += docs/autoiso.cfg
d9d99f
 EXTRA_DIST += docs/grub.cfg
d9d99f
 EXTRA_DIST += docs/osdetect.cfg
d9d99f
+EXTRA_DIST += docs/org.gnu.grub.policy
d9d99f
+EXTRA_DIST += docs/grub-boot-success.service
d9d99f
+EXTRA_DIST += docs/grub-boot-success.timer
d9d99f
 
d9d99f
 EXTRA_DIST += conf/i386-cygwin-img-ld.sc
d9d99f
 
d9d99f
diff --git a/docs/grub-boot-success.service b/docs/grub-boot-success.service
d9d99f
new file mode 100644
b71686
index 000000000..80e79584c
d9d99f
--- /dev/null
d9d99f
+++ b/docs/grub-boot-success.service
d9d99f
@@ -0,0 +1,6 @@
d9d99f
+[Unit]
d9d99f
+Description=Mark boot as successful
d9d99f
+
d9d99f
+[Service]
d9d99f
+Type=oneshot
b71686
+ExecStart=/usr/sbin/grub2-set-bootflag boot_success
d9d99f
diff --git a/docs/grub-boot-success.timer b/docs/grub-boot-success.timer
d9d99f
new file mode 100644
b71686
index 000000000..406f17200
d9d99f
--- /dev/null
d9d99f
+++ b/docs/grub-boot-success.timer
b71686
@@ -0,0 +1,7 @@
d9d99f
+[Unit]
d9d99f
+Description=Mark boot as successful after the user session has run 2 minutes
b71686
+ConditionUser=!@system
b71686
+ConditionVirtualization=!container
d9d99f
+
d9d99f
+[Timer]
d9d99f
+OnActiveSec=2min
d9d99f
diff --git a/util/grub-set-bootflag.1 b/util/grub-set-bootflag.1
d9d99f
new file mode 100644
b71686
index 000000000..57801da22
d9d99f
--- /dev/null
d9d99f
+++ b/util/grub-set-bootflag.1
d9d99f
@@ -0,0 +1,20 @@
d9d99f
+.TH GRUB-SET-BOOTFLAG 1 "Tue Jun 12 2018"
d9d99f
+.SH NAME
d9d99f
+\fBgrub-set-bootflag\fR \(em Set a bootflag in the GRUB environment block.
d9d99f
+
d9d99f
+.SH SYNOPSIS
d9d99f
+\fBgrub-set-bootflag\fR <\fIBOOTFLAG\fR>
d9d99f
+
d9d99f
+.SH DESCRIPTION
d9d99f
+\fBgrub-set-bootflag\fR is a command line to set bootflags in GRUB's
d9d99f
+stored environment.
d9d99f
+
d9d99f
+.SH COMMANDS
d9d99f
+.TP
d9d99f
+\fBBOOTFLAG\fR
d9d99f
+.RS 7
d9d99f
+Bootflag to set, one of \fIboot_success\fR or \fIshow_menu_once\fR.
d9d99f
+.RE
d9d99f
+
d9d99f
+.SH SEE ALSO
d9d99f
+.BR "info grub"