]> git.ipfire.org Git - people/ms/bricklayer.git/commitdiff
bootloader: Don't use Pakfire jail to run GRUB commands
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 29 Nov 2022 14:36:54 +0000 (14:36 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 29 Nov 2022 14:36:54 +0000 (14:36 +0000)
GRUB seems to be very pissy when it does not have full access to the
system - understandably :)

We now create a very simple chroot environment for GRUB to install
itself.

Signed-off-by: root <root@michael.haj.ipfire.org>
src/python/__init__.py
src/python/bootloaders.py

index 860fcc9994e8017d83a214f8cfa72393027b9674..60a3300d9a0c48f070d0bcfcb665e58c446d196f 100644 (file)
@@ -19,6 +19,7 @@
 ###############################################################################
 
 import logging
+import os
 import subprocess
 import sys
 import tempfile
@@ -215,12 +216,16 @@ class Bricklayer(object):
                with open("/etc/os-release") as f:
                        return util.config_read(f)
 
-       def command(self, command, error_ok=False, interactive=False):
+       def command(self, command, error_ok=False, interactive=False, chroot=False, bind=None):
                """
                        Runs a command in a shell environment
                """
                log.debug("Running command: %s" % " ".join(command))
 
+               # Run this command in the installed environment if requested
+               if chroot:
+                       command = ["chroot", self.root] + command
+
                args = {}
 
                if not interactive:
@@ -229,8 +234,19 @@ class Bricklayer(object):
                                "stderr" : subprocess.STDOUT,
                        })
 
-               # Execute the command
-               p = subprocess.run(command, **args)
+               try:
+                       # Bind-mount things
+                       if bind:
+                               self._bind(bind)
+
+                       # Execute the command
+                       p = subprocess.run(command, **args)
+
+               finally:
+                       # If the command is successful or not, we will always need to unbind
+                       # anything that has been mounted before.
+                       if bind:
+                               self._unbind(bind)
 
                # Check the return code (raises CalledProcessError on non-zero)
                if not error_ok:
@@ -249,6 +265,38 @@ class Bricklayer(object):
 
                return output
 
+       def _bind(self, mountpoints):
+               """
+                       Bind-mounts the given mountpoints
+               """
+               for source in mountpoints:
+                       if not source.startswith("/"):
+                               raise ValueError("Mountpoints must be absolute paths")
+
+                       target = os.path.join(self.root, source[1:])
+
+                       # Make sure we mount into our root
+                       assert(target.startswith(self.root))
+
+                       # Perform mount
+                       self.command(["mount", "--bind", source, target])
+
+       def _unbind(self, mountpoints):
+               """
+                       Umounts any bind-mounted mountpoints
+               """
+               for source in reversed(mountpoints):
+                       if not source.startswith("/"):
+                               continue
+
+                       target = os.path.join(self.root, source[1:])
+
+                       # Make sure we mount into our root
+                       assert(target.startswith(self.root))
+
+                       # Perform umount
+                       self.command(["umount", target], error_ok=True)
+
        def setup_pakfire(self, **kwargs):
                """
                        Calls Pakfire and has it load its configuration
index d1cb69fee26fd5ba69d278070984528e09a1d260..d8cb9064351eb3aeadd6070f646eb778ff40a29c 100644 (file)
@@ -82,15 +82,12 @@ class Grub(Bootloader):
        requires_bootldr_partition = True
 
        def install(self):
-               # Initialize Pakfire
-               pakfire = self.bricklayer.setup_pakfire()
-
                # Install GRUB2 on all disks that have been selected
                for disk in self.bricklayer.disks.selected:
-                       self.install_on_disk(disk, pakfire)
+                       self.install_on_disk(disk)
 
                # Install configuration
-               self.install_configuration(pakfire)
+               self.install_configuration()
 
        @property
        def grub_arch(self):
@@ -102,23 +99,21 @@ class Grub(Bootloader):
                        "--target=%s" % self.grub_arch,
                ]
 
-       def install_on_disk(self, disk, pakfire):
+       def install_on_disk(self, disk):
                log.info("Installing GRUB on %s" % disk.path)
 
-               # Make /dev available in the jail
-               # XXX this should be limited to the device nodes we need only
-               bind = ["/dev"]
-
-               pakfire.execute(["grub-install"] + self.grub_args + [disk.path], bind=bind)
+               self.bricklayer.command([
+                       "grub-install", "--verbose", "--force", "--recheck",
+                       *self.grub_args, disk.path,
+               ], chroot=True, bind=["/dev", "/proc", "/sys"])
 
-       def install_configuration(self, pakfire):
+       def install_configuration(self):
                """
                        Generates a GRUB configuration file
                """
-               # XXX see above
-               bind = ["/dev"]
-
-               pakfire.execute(["grub-mkconfig", "-o", "/boot/grub/grub.cfg"], bind=bind)
+               self.bricklayer.command([
+                       "grub-mkconfig", "-o", "/boot/grub/grub.cfg",
+               ], chroot=True, bind=["/dev", "/proc", "/sys"])
 
 
 class GrubEFI(Grub):