nalika / rpms / grub2

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