From 04d170bb09c2f50838f38dae4f36f58154272945 Mon Sep 17 00:00:00 2001 From: David Shea Date: Fri, 17 Jul 2015 19:22:53 -0400 Subject: [PATCH] Add a verification step to Lorax.run. After the cleanup step, check that everything in /usr/bin and /usr/sbin can still run. Currently, this just checks that ELF files have everything they need to link, and scripts have an interpreter. Verifying is on by default but can be skipped with --noverify --- src/pylorax/__init__.py | 10 +++++++- src/pylorax/treebuilder.py | 52 +++++++++++++++++++++++++++++++++++++- src/sbin/lorax | 4 ++- 3 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/pylorax/__init__.py b/src/pylorax/__init__.py index b1178d07..cbe66c3c 100644 --- a/src/pylorax/__init__.py +++ b/src/pylorax/__init__.py @@ -156,7 +156,8 @@ class Lorax(BaseLoraxClass): add_templates=None, add_template_vars=None, add_arch_templates=None, - add_arch_template_vars=None): + add_arch_template_vars=None, + verify=True): assert self._configured @@ -289,6 +290,13 @@ class Lorax(BaseLoraxClass): logger.info("cleaning unneeded files") rb.cleanup() + if verify: + logger.info("verifying the installroot") + if not rb.verify(): + sys.exit(1) + else: + logger.info("Skipping verify") + if self.debug: rb.writepkgsizes(joinpaths(logdir, "final-pkgsizes.txt")) diff --git a/src/pylorax/treebuilder.py b/src/pylorax/treebuilder.py index bea9bcba..5d4f8b79 100644 --- a/src/pylorax/treebuilder.py +++ b/src/pylorax/treebuilder.py @@ -23,12 +23,14 @@ logger = logging.getLogger("pylorax.treebuilder") import os, re from os.path import basename from shutil import copytree, copy2 +from pathlib import Path +import itertools from pylorax.sysutils import joinpaths, remove from pylorax.base import DataHolder from pylorax.ltmpl import LoraxTemplateRunner import pylorax.imgutils as imgutils -from pylorax.executils import runcmd, runcmd_output +from pylorax.executils import runcmd, runcmd_output, execWithCapture templatemap = { 'i386': 'x86.tmpl', @@ -143,6 +145,54 @@ class RuntimeBuilder(object): '''Remove unneeded packages and files with runtime-cleanup.tmpl''' self._runner.run("runtime-cleanup.tmpl") + def verify(self): + '''Ensure that contents of the installroot can run''' + status = True + + ELF_MAGIC = b'\x7fELF' + + # Iterate over all files in /usr/bin and /usr/sbin + # For ELF files, gather them into a list and we'll check them all at + # the end. For files with a #!, check them as we go + elf_files = [] + usr_bin = Path(self.vars.root + '/usr/bin') + usr_sbin = Path(self.vars.root + '/usr/sbin') + for path in (str(x) for x in itertools.chain(usr_bin.iterdir(), usr_sbin.iterdir()) \ + if x.is_file()): + with open(path, "rb") as f: + magic = f.read(4) + if magic == ELF_MAGIC: + # Save the path, minus the chroot prefix + elf_files.append(path[len(self.vars.root):]) + elif magic[:2] == b'#!': + # Reopen the file as text and read the first line. + # Open as latin-1 so that stray 8-bit characters don't make + # things blow up. We only really care about ASCII parts. + with open(path, "rt", encoding="latin-1") as f_text: + # Remove the #!, split on space, and take the first part + shabang = f_text.readline()[2:].split()[0] + + # Does the path exist? + if not os.path.exists(self.vars.root + shabang): + logger.error('%s, needed by %s, does not exist', shabang, path) + status = False + + # Now, run ldd on all the ELF files + # Just run ldd once on everything so it isn't logged a million times. + # At least one thing in the list isn't going to be a dynamic executable, + # so use execWithCapture to ignore the exit code. + filename = '' + for line in execWithCapture('ldd', elf_files, root=self.vars.root, + log_output=False, filter_stderr=True).split('\n'): + if line and not line[0].isspace(): + # New filename header, strip the : at the end and save + filename = line[:-1] + elif 'not found' in line: + logger.error('%s, needed by %s, not found', line.split()[0], filename) + status = False + + return status + def writepkgsizes(self, pkgsizefile): '''debugging data: write a big list of pkg sizes''' fobj = open(pkgsizefile, "w") diff --git a/src/sbin/lorax b/src/sbin/lorax index 93e9ed7e..f92aeb96 100755 --- a/src/sbin/lorax +++ b/src/sbin/lorax @@ -114,6 +114,8 @@ def main(args): optional.add_argument("--add-arch-template-var", dest="add_arch_template_vars", action="append", help="Set variable for architecture-specific image", default=[]) + optional.add_argument("--noverify", action="store_false", default=True, dest="verify", + help="Do not verify the install root") # add the show version option parser.add_argument("-V", help="show program's version number and exit", @@ -200,7 +202,7 @@ def main(args): add_template_vars=parsed_add_template_vars, add_arch_templates=opts.add_arch_templates, add_arch_template_vars=parsed_add_arch_template_vars, - remove_temp=True) + remove_temp=True, verify=opts.verify) def get_dnf_base_object(installroot, repositories, mirrorlists=None,