diff --git a/README.debrand b/README.debrand
deleted file mode 100644
index 01c46d2..0000000
--- a/README.debrand
+++ /dev/null
@@ -1,2 +0,0 @@
-Warning: This package was configured for automatic debranding, but the changes
-failed to apply.
diff --git a/SOURCES/sos-bz2011537-estimate-only-option.patch b/SOURCES/sos-bz2011537-estimate-only-option.patch
index c780809..a1a96c4 100644
--- a/SOURCES/sos-bz2011537-estimate-only-option.patch
+++ b/SOURCES/sos-bz2011537-estimate-only-option.patch
@@ -417,3 +417,900 @@ index ef61fb344..e0617b45e 100644
              self.ui_log.info("")
  
          # package up and compress the results
+From f22efe044f1f0565b57d6aeca2081a5227e0312c Mon Sep 17 00:00:00 2001
+From: Jake Hunsaker <jhunsake@redhat.com>
+Date: Mon, 14 Feb 2022 09:37:30 -0500
+Subject: [PATCH] [utilities] Don't try to chroot to /
+
+With the recent fix for sysroot being `None` to always being (correctly)
+`/`, we should guard against situations where `sos_get_command_output()`
+would now try to chroot to `/` before running any command. Incidentally,
+this would also cause our unittests to fail if they were run by a
+non-root user.
+
+Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
+---
+ sos/utilities.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/sos/utilities.py b/sos/utilities.py
+index 6b13415b..d782123a 100644
+--- a/sos/utilities.py
++++ b/sos/utilities.py
+@@ -120,7 +120,7 @@ def sos_get_command_output(command, timeout=TIMEOUT_DEFAULT, stderr=False,
+     # closure are caught in the parent (chroot and chdir are bound from
+     # the enclosing scope).
+     def _child_prep_fn():
+-        if (chroot):
++        if chroot and chroot != '/':
+             os.chroot(chroot)
+         if (chdir):
+             os.chdir(chdir)
+-- 
+2.34.1
+From 3d064102f8ca6662fd9602512e1cb05cf8746dfd Mon Sep 17 00:00:00 2001
+From: Jake Hunsaker <jhunsake@redhat.com>
+Date: Mon, 27 Sep 2021 19:01:16 -0400
+Subject: [PATCH] [Systemd, Policy] Correct InitSystem chrooting when chroot is
+ needed
+
+This commit resolves a situation in which `sos` is being run in a
+container but the `SystemdInit` InitSystem would not properly load
+information from the host, thus causing the `Plugin.is_service*()`
+methods to erroneously fail or return `False`.
+
+Fix this scenario by pulling the `_container_init()` and related logic
+to check for a containerized host sysroot out of the Red Hat specific
+policy and into the base `LinuxPolicy` class so that the init system can
+be initialized with the correct sysroot, which is now used to chroot the
+calls to the relevant `systemctl` commands.
+
+For now, this does impose the use of looking for the `container` env var
+(automatically set by docker, podman, and crio regardless of
+distribution) and the use of the `HOST` env var to read where the host's
+`/` filesystem is mounted within the container. If desired in the
+future, this can be changed to allow policy-specific overrides. For now
+however, this extends host collection via an sos container for all
+distributions currently shipping sos.
+
+Note that this issue only affected the `InitSystem` abstraction for
+loading information about local services, and did not affect init system
+related commands called by plugins as part of those collections.
+
+Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
+---
+ sos/policies/distros/__init__.py      | 28 ++++++++++++++++++++++++++-
+ sos/policies/distros/redhat.py        | 27 +-------------------------
+ sos/policies/init_systems/__init__.py | 13 +++++++++++--
+ sos/policies/init_systems/systemd.py  |  7 ++++---
+ 4 files changed, 43 insertions(+), 32 deletions(-)
+
+diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py
+index f5b9fd5b01..c33a356a75 100644
+--- a/sos/policies/distros/__init__.py
++++ b/sos/policies/distros/__init__.py
+@@ -29,6 +29,10 @@
+ except ImportError:
+     REQUESTS_LOADED = False
+ 
++# Container environment variables for detecting if we're in a container
++ENV_CONTAINER = 'container'
++ENV_HOST_SYSROOT = 'HOST'
++
+ 
+ class LinuxPolicy(Policy):
+     """This policy is meant to be an abc class that provides common
+@@ -69,10 +73,17 @@ def __init__(self, sysroot=None, init=None, probe_runtime=True):
+                                           probe_runtime=probe_runtime)
+         self.init_kernel_modules()
+ 
++        # need to set _host_sysroot before PackageManager()
++        if sysroot:
++            self._container_init()
++            self._host_sysroot = sysroot
++        else:
++            sysroot = self._container_init()
++
+         if init is not None:
+             self.init_system = init
+         elif os.path.isdir("/run/systemd/system/"):
+-            self.init_system = SystemdInit()
++            self.init_system = SystemdInit(chroot=sysroot)
+         else:
+             self.init_system = InitSystem()
+ 
+@@ -130,6 +141,21 @@ def get_local_name(self):
+     def sanitize_filename(self, name):
+         return re.sub(r"[^-a-z,A-Z.0-9]", "", name)
+ 
++    def _container_init(self):
++        """Check if sos is running in a container and perform container
++        specific initialisation based on ENV_HOST_SYSROOT.
++        """
++        if ENV_CONTAINER in os.environ:
++            if os.environ[ENV_CONTAINER] in ['docker', 'oci', 'podman']:
++                self._in_container = True
++        if ENV_HOST_SYSROOT in os.environ:
++            self._host_sysroot = os.environ[ENV_HOST_SYSROOT]
++        use_sysroot = self._in_container and self._host_sysroot is not None
++        if use_sysroot:
++            host_tmp_dir = os.path.abspath(self._host_sysroot + self._tmp_dir)
++            self._tmp_dir = host_tmp_dir
++        return self._host_sysroot if use_sysroot else None
++
+     def init_kernel_modules(self):
+         """Obtain a list of loaded kernel modules to reference later for plugin
+         enablement and SoSPredicate checks
+diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py
+index b3a84336be..3476e21fb2 100644
+--- a/sos/policies/distros/redhat.py
++++ b/sos/policies/distros/redhat.py
+@@ -17,7 +17,7 @@
+ from sos.presets.redhat import (RHEL_PRESETS, ATOMIC_PRESETS, RHV, RHEL,
+                                 CB, RHOSP, RHOCP, RH_CFME, RH_SATELLITE,
+                                 ATOMIC)
+-from sos.policies.distros import LinuxPolicy
++from sos.policies.distros import LinuxPolicy, ENV_HOST_SYSROOT
+ from sos.policies.package_managers.rpm import RpmPackageManager
+ from sos import _sos as _
+ 
+@@ -56,12 +56,6 @@ def __init__(self, sysroot=None, init=None, probe_runtime=True,
+         super(RedHatPolicy, self).__init__(sysroot=sysroot, init=init,
+                                            probe_runtime=probe_runtime)
+         self.usrmove = False
+-        # need to set _host_sysroot before PackageManager()
+-        if sysroot:
+-            self._container_init()
+-            self._host_sysroot = sysroot
+-        else:
+-            sysroot = self._container_init()
+ 
+         self.package_manager = RpmPackageManager(chroot=sysroot,
+                                                  remote_exec=remote_exec)
+@@ -140,21 +134,6 @@ def transform_path(path):
+         else:
+             return files
+ 
+-    def _container_init(self):
+-        """Check if sos is running in a container and perform container
+-        specific initialisation based on ENV_HOST_SYSROOT.
+-        """
+-        if ENV_CONTAINER in os.environ:
+-            if os.environ[ENV_CONTAINER] in ['docker', 'oci', 'podman']:
+-                self._in_container = True
+-        if ENV_HOST_SYSROOT in os.environ:
+-            self._host_sysroot = os.environ[ENV_HOST_SYSROOT]
+-        use_sysroot = self._in_container and self._host_sysroot is not None
+-        if use_sysroot:
+-            host_tmp_dir = os.path.abspath(self._host_sysroot + self._tmp_dir)
+-            self._tmp_dir = host_tmp_dir
+-        return self._host_sysroot if use_sysroot else None
+-
+     def runlevel_by_service(self, name):
+         from subprocess import Popen, PIPE
+         ret = []
+@@ -183,10 +162,6 @@ def get_tmp_dir(self, opt_tmp_dir):
+         return opt_tmp_dir
+ 
+ 
+-# Container environment variables on Red Hat systems.
+-ENV_CONTAINER = 'container'
+-ENV_HOST_SYSROOT = 'HOST'
+-
+ # Legal disclaimer text for Red Hat products
+ disclaimer_text = """
+ Any information provided to %(vendor)s will be treated in \
+diff --git a/sos/policies/init_systems/__init__.py b/sos/policies/init_systems/__init__.py
+index dd663e6522..beac44cee3 100644
+--- a/sos/policies/init_systems/__init__.py
++++ b/sos/policies/init_systems/__init__.py
+@@ -29,9 +29,14 @@ class InitSystem():
+                       status of services
+     :type query_cmd: ``str``
+ 
++    :param chroot:  Location to chroot to for any command execution, i.e. the
++                    sysroot if we're running in a container
++    :type chroot:   ``str`` or ``None``
++
+     """
+ 
+-    def __init__(self, init_cmd=None, list_cmd=None, query_cmd=None):
++    def __init__(self, init_cmd=None, list_cmd=None, query_cmd=None,
++                 chroot=None):
+         """Initialize a new InitSystem()"""
+ 
+         self.services = {}
+@@ -39,6 +44,7 @@ def __init__(self, init_cmd=None, list_cmd=None, query_cmd=None):
+         self.init_cmd = init_cmd
+         self.list_cmd = "%s %s" % (self.init_cmd, list_cmd) or None
+         self.query_cmd = "%s %s" % (self.init_cmd, query_cmd) or None
++        self.chroot = chroot
+ 
+     def is_enabled(self, name):
+         """Check if given service name is enabled
+@@ -108,7 +114,10 @@ def _query_service(self, name):
+         """Query an individual service"""
+         if self.query_cmd:
+             try:
+-                return sos_get_command_output("%s %s" % (self.query_cmd, name))
++                return sos_get_command_output(
++                    "%s %s" % (self.query_cmd, name),
++                    chroot=self.chroot
++                )
+             except Exception:
+                 return None
+         return None
+diff --git a/sos/policies/init_systems/systemd.py b/sos/policies/init_systems/systemd.py
+index 1b138f97b3..76dc57e27f 100644
+--- a/sos/policies/init_systems/systemd.py
++++ b/sos/policies/init_systems/systemd.py
+@@ -15,11 +15,12 @@
+ class SystemdInit(InitSystem):
+     """InitSystem abstraction for SystemD systems"""
+ 
+-    def __init__(self):
++    def __init__(self, chroot=None):
+         super(SystemdInit, self).__init__(
+             init_cmd='systemctl',
+             list_cmd='list-unit-files --type=service',
+-            query_cmd='status'
++            query_cmd='status',
++            chroot=chroot
+         )
+         self.load_all_services()
+ 
+@@ -30,7 +31,7 @@ def parse_query(self, output):
+         return 'unknown'
+ 
+     def load_all_services(self):
+-        svcs = shell_out(self.list_cmd).splitlines()[1:]
++        svcs = shell_out(self.list_cmd, chroot=self.chroot).splitlines()[1:]
+         for line in svcs:
+             try:
+                 name = line.split('.service')[0]
+From e869bc84c714bfc2249bbcb84e14908049ee42c4 Mon Sep 17 00:00:00 2001
+From: Jake Hunsaker <jhunsake@redhat.com>
+Date: Mon, 27 Sep 2021 12:07:08 -0400
+Subject: [PATCH] [Plugin,utilities] Add sysroot wrapper for os.path.join
+
+Adds a wrapper for `os.path.join()` which accounts for non-/ sysroots,
+like we have done previously for other `os.path` methods. Further
+updates `Plugin()` to use this wrapper where appropriate.
+
+Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
+---
+ sos/report/plugins/__init__.py | 43 +++++++++++++++++-----------------
+ sos/utilities.py               |  6 +++++
+ 2 files changed, 28 insertions(+), 21 deletions(-)
+
+diff --git a/sos/report/plugins/__init__.py b/sos/report/plugins/__init__.py
+index c635b8de9..1f84bca49 100644
+--- a/sos/report/plugins/__init__.py
++++ b/sos/report/plugins/__init__.py
+@@ -13,7 +13,7 @@
+ from sos.utilities import (sos_get_command_output, import_module, grep,
+                            fileobj, tail, is_executable, TIMEOUT_DEFAULT,
+                            path_exists, path_isdir, path_isfile, path_islink,
+-                           listdir)
++                           listdir, path_join)
+ 
+ import os
+ import glob
+@@ -708,19 +708,6 @@ def _log_info(self, msg):
+     def _log_debug(self, msg):
+         self.soslog.debug(self._format_msg(msg))
+ 
+-    def join_sysroot(self, path):
+-        """Join a given path with the configured sysroot
+-
+-        :param path:    The filesystem path that needs to be joined
+-        :type path: ``str``
+-
+-        :returns: The joined filesystem path
+-        :rtype: ``str``
+-        """
+-        if path[0] == os.sep:
+-            path = path[1:]
+-        return os.path.join(self.sysroot, path)
+-
+     def strip_sysroot(self, path):
+         """Remove the configured sysroot from a filesystem path
+ 
+@@ -1176,7 +1163,7 @@ def _copy_dir(self, srcpath):
+ 
+     def _get_dest_for_srcpath(self, srcpath):
+         if self.use_sysroot():
+-            srcpath = self.join_sysroot(srcpath)
++            srcpath = self.path_join(srcpath)
+         for copied in self.copied_files:
+             if srcpath == copied["srcpath"]:
+                 return copied["dstpath"]
+@@ -1284,7 +1271,7 @@ def add_forbidden_path(self, forbidden, recursive=False):
+             forbidden = [forbidden]
+ 
+         if self.use_sysroot():
+-            forbidden = [self.join_sysroot(f) for f in forbidden]
++            forbidden = [self.path_join(f) for f in forbidden]
+ 
+         for forbid in forbidden:
+             self._log_info("adding forbidden path '%s'" % forbid)
+@@ -1438,7 +1425,7 @@ def add_copy_spec(self, copyspecs, sizelimit=None, maxage=None,
+             since = self.get_option('since')
+ 
+         logarchive_pattern = re.compile(r'.*((\.(zip|gz|bz2|xz))|[-.][\d]+)$')
+-        configfile_pattern = re.compile(r"^%s/*" % self.join_sysroot("etc"))
++        configfile_pattern = re.compile(r"^%s/*" % self.path_join("etc"))
+ 
+         if not self.test_predicate(pred=pred):
+             self._log_info("skipped copy spec '%s' due to predicate (%s)" %
+@@ -1468,7 +1455,7 @@ def add_copy_spec(self, copyspecs, sizelimit=None, maxage=None,
+                 return False
+ 
+             if self.use_sysroot():
+-                copyspec = self.join_sysroot(copyspec)
++                copyspec = self.path_join(copyspec)
+ 
+             files = self._expand_copy_spec(copyspec)
+ 
+@@ -1683,7 +1670,7 @@ def _add_device_cmd(self, cmds, devices, timeout=None, sizelimit=None,
+                 if not _dev_ok:
+                     continue
+                 if prepend_path:
+-                    device = os.path.join(prepend_path, device)
++                    device = self.path_join(prepend_path, device)
+                 _cmd = cmd % {'dev': device}
+                 self._add_cmd_output(cmd=_cmd, timeout=timeout,
+                                      sizelimit=sizelimit, chroot=chroot,
+@@ -2592,7 +2579,7 @@ def __expand(paths):
+                     if self.path_isfile(path) or self.path_islink(path):
+                         found_paths.append(path)
+                     elif self.path_isdir(path) and self.listdir(path):
+-                        found_paths.extend(__expand(os.path.join(path, '*')))
++                        found_paths.extend(__expand(self.path_join(path, '*')))
+                     else:
+                         found_paths.append(path)
+                 except PermissionError:
+@@ -2608,7 +2595,7 @@ def __expand(paths):
+         if (os.access(copyspec, os.R_OK) and self.path_isdir(copyspec) and
+                 self.listdir(copyspec)):
+             # the directory exists and is non-empty, recurse through it
+-            copyspec = os.path.join(copyspec, '*')
++            copyspec = self.path_join(copyspec, '*')
+         expanded = glob.glob(copyspec, recursive=True)
+         recursed_files = []
+         for _path in expanded:
+@@ -2877,6 +2864,20 @@ def listdir(self, path):
+         """
+         return listdir(path, self.commons['cmdlineopts'].sysroot)
+ 
++    def path_join(self, path, *p):
++        """Helper to call the sos.utilities wrapper that allows the
++        corresponding `os` call to account for sysroot
++
++        :param path:    The leading path passed to os.path.join()
++        :type path:     ``str``
++
++        :param p:       Following path section(s) to be joined with ``path``,
++                        an empty parameter will result in a path that ends with
++                        a separator
++        :type p:        ``str``
++        """
++        return path_join(path, *p, sysroot=self.sysroot)
++
+     def postproc(self):
+         """Perform any postprocessing. To be replaced by a plugin if required.
+         """
+diff --git a/sos/utilities.py b/sos/utilities.py
+index c940e066d..b75751539 100644
+--- a/sos/utilities.py
++++ b/sos/utilities.py
+@@ -242,6 +242,12 @@ def listdir(path, sysroot):
+     return _os_wrapper(path, sysroot, 'listdir', os)
+ 
+ 
++def path_join(path, *p, sysroot=os.sep):
++    if not path.startswith(sysroot):
++        path = os.path.join(sysroot, path.lstrip(os.sep))
++    return os.path.join(path, *p)
++
++
+ class AsyncReader(threading.Thread):
+     """Used to limit command output to a given size without deadlocking
+     sos.
+From 9596473d1779b9c48e9923c220aaf2b8d9b3bebf Mon Sep 17 00:00:00 2001
+From: Jake Hunsaker <jhunsake@redhat.com>
+Date: Thu, 18 Nov 2021 13:17:14 -0500
+Subject: [PATCH] [global] Align sysroot determination and usage across sos
+
+The determination of sysroot - being automatic, user-specified, or
+controlled via environment variables in a container - has gotten muddied
+over time. This has resulted in different parts of the project;
+`Policy`, `Plugin`, `SoSComponent`, etc... to not always be in sync when
+sysroot is not `/`, thus causing varying and unexpected/unintended
+behavior.
+
+Fix this by only determining sysroot within `Policy()` initialization,
+and then using that determination across all aspects of the project that
+use or reference sysroot.
+
+This results in several changes:
+
+- `PackageManager()` will now (again) correctly reference host package
+  lists when sos is run in a container.
+
+- `ContainerRuntime()` is now able to activate when sos is running in a
+  container.
+
+- Plugins will now properly use sysroot for _all_ plugin enablement
+  triggers.
+
+- Plugins, Policy, and SoSComponents now all reference the
+  `self.sysroot` variable, rather than changing between `sysroot`.
+`_host_sysroot`, and `commons['sysroot']`. `_host_sysroot` has been
+removed from `Policy`.
+
+Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
+---
+ sos/archive.py                    |  2 +-
+ sos/component.py                  |  2 +-
+ sos/policies/__init__.py          | 11 +----------
+ sos/policies/distros/__init__.py  | 33 +++++++++++++++++++------------
+ sos/policies/distros/debian.py    |  2 +-
+ sos/policies/distros/redhat.py    |  3 +--
+ sos/policies/runtimes/__init__.py | 15 +++++++++-----
+ sos/policies/runtimes/docker.py   |  4 ++--
+ sos/report/__init__.py            |  6 ++----
+ sos/report/plugins/__init__.py    | 22 +++++++++++----------
+ sos/report/plugins/unpackaged.py  |  7 ++++---
+ sos/utilities.py                  | 13 ++++++++----
+ 12 files changed, 64 insertions(+), 56 deletions(-)
+
+diff --git a/sos/archive.py b/sos/archive.py
+index b02b247595..e3c68b7789 100644
+--- a/sos/archive.py
++++ b/sos/archive.py
+@@ -153,7 +153,7 @@ def dest_path(self, name):
+         return (os.path.join(self._archive_root, name))
+ 
+     def join_sysroot(self, path):
+-        if path.startswith(self.sysroot):
++        if not self.sysroot or path.startswith(self.sysroot):
+             return path
+         if path[0] == os.sep:
+             path = path[1:]
+diff --git a/sos/component.py b/sos/component.py
+index 5ac6e47f4f..dba0aabf2b 100644
+--- a/sos/component.py
++++ b/sos/component.py
+@@ -109,7 +109,7 @@ def __init__(self, parser, parsed_args, cmdline_args):
+             try:
+                 import sos.policies
+                 self.policy = sos.policies.load(sysroot=self.opts.sysroot)
+-                self.sysroot = self.policy.host_sysroot()
++                self.sysroot = self.policy.sysroot
+             except KeyboardInterrupt:
+                 self._exit(0)
+             self._is_root = self.policy.is_root()
+diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py
+index fb8db1d724..ef9188deb4 100644
+--- a/sos/policies/__init__.py
++++ b/sos/policies/__init__.py
+@@ -110,7 +110,6 @@ class Policy(object):
+     presets = {"": PresetDefaults()}
+     presets_path = PRESETS_PATH
+     _in_container = False
+-    _host_sysroot = '/'
+ 
+     def __init__(self, sysroot=None, probe_runtime=True):
+         """Subclasses that choose to override this initializer should call
+@@ -124,7 +123,7 @@ def __init__(self, sysroot=None, probe_runtime=True):
+         self.package_manager = PackageManager()
+         self.valid_subclasses = [IndependentPlugin]
+         self.set_exec_path()
+-        self._host_sysroot = sysroot
++        self.sysroot = sysroot
+         self.register_presets(GENERIC_PRESETS)
+ 
+     def check(self, remote=''):
+@@ -177,14 +176,6 @@ def in_container(self):
+         """
+         return self._in_container
+ 
+-    def host_sysroot(self):
+-        """Get the host's default sysroot
+-
+-        :returns: Host sysroot
+-        :rtype: ``str`` or ``None``
+-        """
+-        return self._host_sysroot
+-
+     def dist_version(self):
+         """
+         Return the OS version
+diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py
+index 7bdc81b852..c69fc1e73c 100644
+--- a/sos/policies/distros/__init__.py
++++ b/sos/policies/distros/__init__.py
+@@ -71,19 +71,18 @@ class LinuxPolicy(Policy):
+     def __init__(self, sysroot=None, init=None, probe_runtime=True):
+         super(LinuxPolicy, self).__init__(sysroot=sysroot,
+                                           probe_runtime=probe_runtime)
+-        self.init_kernel_modules()
+ 
+-        # need to set _host_sysroot before PackageManager()
+         if sysroot:
+-            self._container_init()
+-            self._host_sysroot = sysroot
++            self.sysroot = sysroot
+         else:
+-            sysroot = self._container_init()
++            self.sysroot = self._container_init()
++
++        self.init_kernel_modules()
+ 
+         if init is not None:
+             self.init_system = init
+         elif os.path.isdir("/run/systemd/system/"):
+-            self.init_system = SystemdInit(chroot=sysroot)
++            self.init_system = SystemdInit(chroot=self.sysroot)
+         else:
+             self.init_system = InitSystem()
+ 
+@@ -149,27 +148,30 @@ def _container_init(self):
+             if os.environ[ENV_CONTAINER] in ['docker', 'oci', 'podman']:
+                 self._in_container = True
+         if ENV_HOST_SYSROOT in os.environ:
+-            self._host_sysroot = os.environ[ENV_HOST_SYSROOT]
+-        use_sysroot = self._in_container and self._host_sysroot is not None
++            _host_sysroot = os.environ[ENV_HOST_SYSROOT]
++        use_sysroot = self._in_container and _host_sysroot is not None
+         if use_sysroot:
+-            host_tmp_dir = os.path.abspath(self._host_sysroot + self._tmp_dir)
++            host_tmp_dir = os.path.abspath(_host_sysroot + self._tmp_dir)
+             self._tmp_dir = host_tmp_dir
+-        return self._host_sysroot if use_sysroot else None
++        return _host_sysroot if use_sysroot else None
+ 
+     def init_kernel_modules(self):
+         """Obtain a list of loaded kernel modules to reference later for plugin
+         enablement and SoSPredicate checks
+         """
+         self.kernel_mods = []
++        release = os.uname().release
+ 
+         # first load modules from lsmod
+-        lines = shell_out("lsmod", timeout=0).splitlines()
++        lines = shell_out("lsmod", timeout=0, chroot=self.sysroot).splitlines()
+         self.kernel_mods.extend([
+             line.split()[0].strip() for line in lines[1:]
+         ])
+ 
+         # next, include kernel builtins
+-        builtins = "/usr/lib/modules/%s/modules.builtin" % os.uname().release
++        builtins = self.join_sysroot(
++            "/usr/lib/modules/%s/modules.builtin" % release
++        )
+         try:
+             with open(builtins, "r") as mfile:
+                 for line in mfile:
+@@ -186,7 +188,7 @@ def init_kernel_modules(self):
+             'dm_mod': 'CONFIG_BLK_DEV_DM'
+         }
+ 
+-        booted_config = "/boot/config-%s" % os.uname().release
++        booted_config = self.join_sysroot("/boot/config-%s" % release)
+         kconfigs = []
+         try:
+             with open(booted_config, "r") as kfile:
+@@ -200,6 +202,11 @@ def init_kernel_modules(self):
+             if config_strings[builtin] in kconfigs:
+                 self.kernel_mods.append(builtin)
+ 
++    def join_sysroot(self, path):
++        if self.sysroot and self.sysroot != '/':
++            path = os.path.join(self.sysroot, path.lstrip('/'))
++        return path
++
+     def pre_work(self):
+         # this method will be called before the gathering begins
+ 
+diff --git a/sos/policies/distros/debian.py b/sos/policies/distros/debian.py
+index 95b389a65e..639fd5eba3 100644
+--- a/sos/policies/distros/debian.py
++++ b/sos/policies/distros/debian.py
+@@ -27,7 +27,7 @@ def __init__(self, sysroot=None, init=None, probe_runtime=True,
+                  remote_exec=None):
+         super(DebianPolicy, self).__init__(sysroot=sysroot, init=init,
+                                            probe_runtime=probe_runtime)
+-        self.package_manager = DpkgPackageManager(chroot=sysroot,
++        self.package_manager = DpkgPackageManager(chroot=self.sysroot,
+                                                   remote_exec=remote_exec)
+         self.valid_subclasses += [DebianPlugin]
+ 
+diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py
+index eb44240736..4b14abaf3a 100644
+--- a/sos/policies/distros/redhat.py
++++ b/sos/policies/distros/redhat.py
+@@ -42,7 +42,6 @@ class RedHatPolicy(LinuxPolicy):
+     _redhat_release = '/etc/redhat-release'
+     _tmp_dir = "/var/tmp"
+     _in_container = False
+-    _host_sysroot = '/'
+     default_scl_prefix = '/opt/rh'
+     name_pattern = 'friendly'
+     upload_url = None
+@@ -57,7 +56,7 @@ def __init__(self, sysroot=None, init=None, probe_runtime=True,
+                                            probe_runtime=probe_runtime)
+         self.usrmove = False
+ 
+-        self.package_manager = RpmPackageManager(chroot=sysroot,
++        self.package_manager = RpmPackageManager(chroot=self.sysroot,
+                                                  remote_exec=remote_exec)
+ 
+         self.valid_subclasses += [RedHatPlugin]
+diff --git a/sos/policies/runtimes/__init__.py b/sos/policies/runtimes/__init__.py
+index f28d6a1df3..2e60ad2361 100644
+--- a/sos/policies/runtimes/__init__.py
++++ b/sos/policies/runtimes/__init__.py
+@@ -64,7 +64,7 @@ def check_is_active(self):
+         :returns: ``True`` if the runtime is active, else ``False``
+         :rtype: ``bool``
+         """
+-        if is_executable(self.binary):
++        if is_executable(self.binary, self.policy.sysroot):
+             self.active = True
+             return True
+         return False
+@@ -78,7 +78,7 @@ def get_containers(self, get_all=False):
+         containers = []
+         _cmd = "%s ps %s" % (self.binary, '-a' if get_all else '')
+         if self.active:
+-            out = sos_get_command_output(_cmd)
++            out = sos_get_command_output(_cmd, chroot=self.policy.sysroot)
+             if out['status'] == 0:
+                 for ent in out['output'].splitlines()[1:]:
+                     ent = ent.split()
+@@ -112,8 +112,10 @@ def get_images(self):
+         images = []
+         fmt = '{{lower .Repository}}:{{lower .Tag}} {{lower .ID}}'
+         if self.active:
+-            out = sos_get_command_output("%s images --format '%s'"
+-                                         % (self.binary, fmt))
++            out = sos_get_command_output(
++                "%s images --format '%s'" % (self.binary, fmt),
++                chroot=self.policy.sysroot
++            )
+             if out['status'] == 0:
+                 for ent in out['output'].splitlines():
+                     ent = ent.split()
+@@ -129,7 +131,10 @@ def get_volumes(self):
+         """
+         vols = []
+         if self.active:
+-            out = sos_get_command_output("%s volume ls" % self.binary)
++            out = sos_get_command_output(
++                "%s volume ls" % self.binary,
++                chroot=self.policy.sysroot
++            )
+             if out['status'] == 0:
+                 for ent in out['output'].splitlines()[1:]:
+                     ent = ent.split()
+diff --git a/sos/policies/runtimes/docker.py b/sos/policies/runtimes/docker.py
+index 759dfaf6a0..e81f580ec3 100644
+--- a/sos/policies/runtimes/docker.py
++++ b/sos/policies/runtimes/docker.py
+@@ -18,9 +18,9 @@ class DockerContainerRuntime(ContainerRuntime):
+     name = 'docker'
+     binary = 'docker'
+ 
+-    def check_is_active(self):
++    def check_is_active(self, sysroot=None):
+         # the daemon must be running
+-        if (is_executable('docker') and
++        if (is_executable('docker', sysroot) and
+                 (self.policy.init_system.is_running('docker') or
+                  self.policy.init_system.is_running('snap.docker.dockerd'))):
+             self.active = True
+diff --git a/sos/report/__init__.py b/sos/report/__init__.py
+index a4c92accd3..a6c72778fc 100644
+--- a/sos/report/__init__.py
++++ b/sos/report/__init__.py
+@@ -173,14 +173,12 @@ def __init__(self, parser, args, cmdline):
+         self._set_directories()
+ 
+         msg = "default"
+-        host_sysroot = self.policy.host_sysroot()
++        self.sysroot = self.policy.sysroot
+         # set alternate system root directory
+         if self.opts.sysroot:
+             msg = "cmdline"
+-            self.sysroot = self.opts.sysroot
+-        elif self.policy.in_container() and host_sysroot != os.sep:
++        elif self.policy.in_container() and self.sysroot != os.sep:
+             msg = "policy"
+-            self.sysroot = host_sysroot
+         self.soslog.debug("set sysroot to '%s' (%s)" % (self.sysroot, msg))
+ 
+         if self.opts.chroot not in chroot_modes:
+diff --git a/sos/report/plugins/__init__.py b/sos/report/plugins/__init__.py
+index 46028bb124..e180ae1727 100644
+--- a/sos/report/plugins/__init__.py
++++ b/sos/report/plugins/__init__.py
+@@ -724,7 +724,7 @@ def strip_sysroot(self, path):
+         """
+         if not self.use_sysroot():
+             return path
+-        if path.startswith(self.sysroot):
++        if self.sysroot and path.startswith(self.sysroot):
+             return path[len(self.sysroot):]
+         return path
+ 
+@@ -743,8 +743,10 @@ def tmp_in_sysroot(self):
+                   ``False``
+         :rtype: ``bool``
+         """
+-        paths = [self.sysroot, self.archive.get_tmp_dir()]
+-        return os.path.commonprefix(paths) == self.sysroot
++        # if sysroot is still None, that implies '/'
++        _sysroot = self.sysroot or '/'
++        paths = [_sysroot, self.archive.get_tmp_dir()]
++        return os.path.commonprefix(paths) == _sysroot
+ 
+     def is_installed(self, package_name):
+         """Is the package $package_name installed?
+@@ -2621,7 +2623,7 @@ def __expand(paths):
+         return list(set(expanded))
+ 
+     def _collect_copy_specs(self):
+-        for path in self.copy_paths:
++        for path in sorted(self.copy_paths, reverse=True):
+             self._log_info("collecting path '%s'" % path)
+             self._do_copy_path(path)
+         self.generate_copyspec_tags()
+@@ -2749,7 +2751,7 @@ def _check_plugin_triggers(self, files, packages, commands, services,
+ 
+         return ((any(self.path_exists(fname) for fname in files) or
+                 any(self.is_installed(pkg) for pkg in packages) or
+-                any(is_executable(cmd) for cmd in commands) or
++                any(is_executable(cmd, self.sysroot) for cmd in commands) or
+                 any(self.is_module_loaded(mod) for mod in self.kernel_mods) or
+                 any(self.is_service(svc) for svc in services) or
+                 any(self.container_exists(cntr) for cntr in containers)) and
+@@ -2817,7 +2819,7 @@ def path_exists(self, path):
+         :returns:           True if the path exists in sysroot, else False
+         :rtype:             ``bool``
+         """
+-        return path_exists(path, self.commons['cmdlineopts'].sysroot)
++        return path_exists(path, self.sysroot)
+ 
+     def path_isdir(self, path):
+         """Helper to call the sos.utilities wrapper that allows the
+@@ -2830,7 +2832,7 @@ def path_isdir(self, path):
+         :returns:           True if the path is a dir, else False
+         :rtype:             ``bool``
+         """
+-        return path_isdir(path, self.commons['cmdlineopts'].sysroot)
++        return path_isdir(path, self.sysroot)
+ 
+     def path_isfile(self, path):
+         """Helper to call the sos.utilities wrapper that allows the
+@@ -2843,7 +2845,7 @@ def path_isfile(self, path):
+         :returns:           True if the path is a file, else False
+         :rtype:             ``bool``
+         """
+-        return path_isfile(path, self.commons['cmdlineopts'].sysroot)
++        return path_isfile(path, self.sysroot)
+ 
+     def path_islink(self, path):
+         """Helper to call the sos.utilities wrapper that allows the
+@@ -2856,7 +2858,7 @@ def path_islink(self, path):
+         :returns:           True if the path is a link, else False
+         :rtype:             ``bool``
+         """
+-        return path_islink(path, self.commons['cmdlineopts'].sysroot)
++        return path_islink(path, self.sysroot)
+ 
+     def listdir(self, path):
+         """Helper to call the sos.utilities wrapper that allows the
+@@ -2869,7 +2871,7 @@ def listdir(self, path):
+         :returns:           Contents of path, if it is a directory
+         :rtype:             ``list``
+         """
+-        return listdir(path, self.commons['cmdlineopts'].sysroot)
++        return listdir(path, self.sysroot)
+ 
+     def path_join(self, path, *p):
+         """Helper to call the sos.utilities wrapper that allows the
+diff --git a/sos/report/plugins/unpackaged.py b/sos/report/plugins/unpackaged.py
+index 772b1d1fbb..24203c4b13 100644
+--- a/sos/report/plugins/unpackaged.py
++++ b/sos/report/plugins/unpackaged.py
+@@ -58,10 +58,11 @@ def format_output(files):
+             """
+             expanded = []
+             for f in files:
+-                if self.path_islink(f):
+-                    expanded.append("{} -> {}".format(f, os.readlink(f)))
++                fp = self.path_join(f)
++                if self.path_islink(fp):
++                    expanded.append("{} -> {}".format(fp, os.readlink(fp)))
+                 else:
+-                    expanded.append(f)
++                    expanded.append(fp)
+             return expanded
+ 
+         # Check command predicate to avoid costly processing
+diff --git a/sos/utilities.py b/sos/utilities.py
+index b757515397..d66309334b 100644
+--- a/sos/utilities.py
++++ b/sos/utilities.py
+@@ -96,11 +96,15 @@ def grep(pattern, *files_or_paths):
+     return matches
+ 
+ 
+-def is_executable(command):
++def is_executable(command, sysroot=None):
+     """Returns if a command matches an executable on the PATH"""
+ 
+     paths = os.environ.get("PATH", "").split(os.path.pathsep)
+     candidates = [command] + [os.path.join(p, command) for p in paths]
++    if sysroot:
++        candidates += [
++            os.path.join(sysroot, c.lstrip('/')) for c in candidates
++        ]
+     return any(os.access(path, os.X_OK) for path in candidates)
+ 
+ 
+@@ -216,8 +220,9 @@ def get_human_readable(size, precision=2):
+ 
+ 
+ def _os_wrapper(path, sysroot, method, module=os.path):
+-    if sysroot not in [None, '/']:
+-        path = os.path.join(sysroot, path.lstrip('/'))
++    if sysroot and sysroot != os.sep:
++        if not path.startswith(sysroot):
++            path = os.path.join(sysroot, path.lstrip('/'))
+     _meth = getattr(module, method)
+     return _meth(path)
+ 
+@@ -243,7 +248,7 @@ def listdir(path, sysroot):
+ 
+ 
+ def path_join(path, *p, sysroot=os.sep):
+-    if not path.startswith(sysroot):
++    if sysroot and not path.startswith(sysroot):
+         path = os.path.join(sysroot, path.lstrip(os.sep))
+     return os.path.join(path, *p)
+ 
+From a43124e1f6217107838eed4d70339d100cbbc77a Mon Sep 17 00:00:00 2001
+From: Pavel Moravec <pmoravec@redhat.com>
+Date: Wed, 9 Feb 2022 19:45:27 +0100
+Subject: [PATCH] [policies] Set fallback to None sysroot
+
+9596473 commit added a regression allowing to set sysroot to None
+when running sos report on a regular system (outside a container). In
+such a case, we need to fallback to '/' sysroot.
+
+Resolves: #2846
+
+Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
+---
+ sos/policies/distros/__init__.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py
+index f3c1de11..9048f1c4 100644
+--- a/sos/policies/distros/__init__.py
++++ b/sos/policies/distros/__init__.py
+@@ -78,7 +78,7 @@ class LinuxPolicy(Policy):
+         if sysroot:
+             self.sysroot = sysroot
+         else:
+-            self.sysroot = self._container_init()
++            self.sysroot = self._container_init() or '/'
+
+         self.init_kernel_modules()
+
+-- 
+2.34.1
+
diff --git a/SOURCES/sos-bz2024893-cleaner-hostnames-improvements.patch b/SOURCES/sos-bz2024893-cleaner-hostnames-improvements.patch
index d257fdf..b129f9e 100644
--- a/SOURCES/sos-bz2024893-cleaner-hostnames-improvements.patch
+++ b/SOURCES/sos-bz2024893-cleaner-hostnames-improvements.patch
@@ -1797,3 +1797,33 @@ index 229c7de4..3208a655 100644
 -- 
 2.31.1
 
+From 7ebb2ce0bcd13c1b3aada648aceb20b5aff636d9 Mon Sep 17 00:00:00 2001
+From: Jake Hunsaker <jhunsake@redhat.com>
+Date: Tue, 15 Feb 2022 14:18:02 -0500
+Subject: [PATCH] [host] Skip entire /etc/sos/cleaner directory
+
+While `default_mapping` is typically the only file expected under
+`/etc/sos/cleaner/` it is possible for other mapping files (such as
+backups) to appear there.
+
+Make the `add_forbidden_path()` spec here target the entire cleaner
+directory to avoid ever capturing these map files.
+
+Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
+---
+ sos/report/plugins/host.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/sos/report/plugins/host.py b/sos/report/plugins/host.py
+index 5e21da7b8e..95a3b9cd95 100644
+--- a/sos/report/plugins/host.py
++++ b/sos/report/plugins/host.py
+@@ -20,7 +20,7 @@ class Host(Plugin, IndependentPlugin):
+ 
+     def setup(self):
+ 
+-        self.add_forbidden_path('/etc/sos/cleaner/default_mapping')
++        self.add_forbidden_path('/etc/sos/cleaner')
+ 
+         self.add_cmd_output('hostname', root_symlink='hostname')
+         self.add_cmd_output('uptime', root_symlink='uptime')
diff --git a/SOURCES/sos-bz2037350-ocp-backports.patch b/SOURCES/sos-bz2037350-ocp-backports.patch
index 5a44250..3e53e93 100644
--- a/SOURCES/sos-bz2037350-ocp-backports.patch
+++ b/SOURCES/sos-bz2037350-ocp-backports.patch
@@ -1608,159 +1608,6 @@ index 00000000..a4897f19
 -- 
 2.31.1
 
-From e869bc84c714bfc2249bbcb84e14908049ee42c4 Mon Sep 17 00:00:00 2001
-From: Jake Hunsaker <jhunsake@redhat.com>
-Date: Mon, 27 Sep 2021 12:07:08 -0400
-Subject: [PATCH 1/2] [Plugin,utilities] Add sysroot wrapper for os.path.join
-
-Adds a wrapper for `os.path.join()` which accounts for non-/ sysroots,
-like we have done previously for other `os.path` methods. Further
-updates `Plugin()` to use this wrapper where appropriate.
-
-Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
----
- sos/report/plugins/__init__.py | 43 +++++++++++++++++-----------------
- sos/utilities.py               |  6 +++++
- 2 files changed, 28 insertions(+), 21 deletions(-)
-
-diff --git a/sos/report/plugins/__init__.py b/sos/report/plugins/__init__.py
-index c635b8de..1f84bca4 100644
---- a/sos/report/plugins/__init__.py
-+++ b/sos/report/plugins/__init__.py
-@@ -13,7 +13,7 @@
- from sos.utilities import (sos_get_command_output, import_module, grep,
-                            fileobj, tail, is_executable, TIMEOUT_DEFAULT,
-                            path_exists, path_isdir, path_isfile, path_islink,
--                           listdir)
-+                           listdir, path_join)
- 
- import os
- import glob
-@@ -708,19 +708,6 @@ class Plugin():
-     def _log_debug(self, msg):
-         self.soslog.debug(self._format_msg(msg))
- 
--    def join_sysroot(self, path):
--        """Join a given path with the configured sysroot
--
--        :param path:    The filesystem path that needs to be joined
--        :type path: ``str``
--
--        :returns: The joined filesystem path
--        :rtype: ``str``
--        """
--        if path[0] == os.sep:
--            path = path[1:]
--        return os.path.join(self.sysroot, path)
--
-     def strip_sysroot(self, path):
-         """Remove the configured sysroot from a filesystem path
- 
-@@ -1176,7 +1163,7 @@ class Plugin():
- 
-     def _get_dest_for_srcpath(self, srcpath):
-         if self.use_sysroot():
--            srcpath = self.join_sysroot(srcpath)
-+            srcpath = self.path_join(srcpath)
-         for copied in self.copied_files:
-             if srcpath == copied["srcpath"]:
-                 return copied["dstpath"]
-@@ -1284,7 +1271,7 @@ class Plugin():
-             forbidden = [forbidden]
- 
-         if self.use_sysroot():
--            forbidden = [self.join_sysroot(f) for f in forbidden]
-+            forbidden = [self.path_join(f) for f in forbidden]
- 
-         for forbid in forbidden:
-             self._log_info("adding forbidden path '%s'" % forbid)
-@@ -1438,7 +1425,7 @@ class Plugin():
-             since = self.get_option('since')
- 
-         logarchive_pattern = re.compile(r'.*((\.(zip|gz|bz2|xz))|[-.][\d]+)$')
--        configfile_pattern = re.compile(r"^%s/*" % self.join_sysroot("etc"))
-+        configfile_pattern = re.compile(r"^%s/*" % self.path_join("etc"))
- 
-         if not self.test_predicate(pred=pred):
-             self._log_info("skipped copy spec '%s' due to predicate (%s)" %
-@@ -1468,7 +1455,7 @@ class Plugin():
-                 return False
- 
-             if self.use_sysroot():
--                copyspec = self.join_sysroot(copyspec)
-+                copyspec = self.path_join(copyspec)
- 
-             files = self._expand_copy_spec(copyspec)
- 
-@@ -1683,7 +1670,7 @@ class Plugin():
-                 if not _dev_ok:
-                     continue
-                 if prepend_path:
--                    device = os.path.join(prepend_path, device)
-+                    device = self.path_join(prepend_path, device)
-                 _cmd = cmd % {'dev': device}
-                 self._add_cmd_output(cmd=_cmd, timeout=timeout,
-                                      sizelimit=sizelimit, chroot=chroot,
-@@ -2592,7 +2579,7 @@ class Plugin():
-                     if self.path_isfile(path) or self.path_islink(path):
-                         found_paths.append(path)
-                     elif self.path_isdir(path) and self.listdir(path):
--                        found_paths.extend(__expand(os.path.join(path, '*')))
-+                        found_paths.extend(__expand(self.path_join(path, '*')))
-                     else:
-                         found_paths.append(path)
-                 except PermissionError:
-@@ -2608,7 +2595,7 @@ class Plugin():
-         if (os.access(copyspec, os.R_OK) and self.path_isdir(copyspec) and
-                 self.listdir(copyspec)):
-             # the directory exists and is non-empty, recurse through it
--            copyspec = os.path.join(copyspec, '*')
-+            copyspec = self.path_join(copyspec, '*')
-         expanded = glob.glob(copyspec, recursive=True)
-         recursed_files = []
-         for _path in expanded:
-@@ -2877,6 +2864,20 @@ class Plugin():
-         """
-         return listdir(path, self.commons['cmdlineopts'].sysroot)
- 
-+    def path_join(self, path, *p):
-+        """Helper to call the sos.utilities wrapper that allows the
-+        corresponding `os` call to account for sysroot
-+
-+        :param path:    The leading path passed to os.path.join()
-+        :type path:     ``str``
-+
-+        :param p:       Following path section(s) to be joined with ``path``,
-+                        an empty parameter will result in a path that ends with
-+                        a separator
-+        :type p:        ``str``
-+        """
-+        return path_join(path, *p, sysroot=self.sysroot)
-+
-     def postproc(self):
-         """Perform any postprocessing. To be replaced by a plugin if required.
-         """
-diff --git a/sos/utilities.py b/sos/utilities.py
-index c940e066..b7575153 100644
---- a/sos/utilities.py
-+++ b/sos/utilities.py
-@@ -242,6 +242,12 @@ def listdir(path, sysroot):
-     return _os_wrapper(path, sysroot, 'listdir', os)
- 
- 
-+def path_join(path, *p, sysroot=os.sep):
-+    if not path.startswith(sysroot):
-+        path = os.path.join(sysroot, path.lstrip(os.sep))
-+    return os.path.join(path, *p)
-+
-+
- class AsyncReader(threading.Thread):
-     """Used to limit command output to a given size without deadlocking
-     sos.
--- 
-2.31.1
-
-
 From 07d96d52ef69b9f8fe1ef32a1b88089d31c33fe8 Mon Sep 17 00:00:00 2001
 From: Jake Hunsaker <jhunsake@redhat.com>
 Date: Mon, 27 Sep 2021 12:28:27 -0400
@@ -3679,697 +3526,6 @@ index e84b52da..1bfa741f 100644
 -- 
 2.31.1
 
-From 3d064102f8ca6662fd9602512e1cb05cf8746dfd Mon Sep 17 00:00:00 2001
-From: Jake Hunsaker <jhunsake@redhat.com>
-Date: Mon, 27 Sep 2021 19:01:16 -0400
-Subject: [PATCH] [Systemd, Policy] Correct InitSystem chrooting when chroot is
- needed
-
-This commit resolves a situation in which `sos` is being run in a
-container but the `SystemdInit` InitSystem would not properly load
-information from the host, thus causing the `Plugin.is_service*()`
-methods to erroneously fail or return `False`.
-
-Fix this scenario by pulling the `_container_init()` and related logic
-to check for a containerized host sysroot out of the Red Hat specific
-policy and into the base `LinuxPolicy` class so that the init system can
-be initialized with the correct sysroot, which is now used to chroot the
-calls to the relevant `systemctl` commands.
-
-For now, this does impose the use of looking for the `container` env var
-(automatically set by docker, podman, and crio regardless of
-distribution) and the use of the `HOST` env var to read where the host's
-`/` filesystem is mounted within the container. If desired in the
-future, this can be changed to allow policy-specific overrides. For now
-however, this extends host collection via an sos container for all
-distributions currently shipping sos.
-
-Note that this issue only affected the `InitSystem` abstraction for
-loading information about local services, and did not affect init system
-related commands called by plugins as part of those collections.
-
-Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
----
- sos/policies/distros/__init__.py      | 28 ++++++++++++++++++++++++++-
- sos/policies/distros/redhat.py        | 27 +-------------------------
- sos/policies/init_systems/__init__.py | 13 +++++++++++--
- sos/policies/init_systems/systemd.py  |  7 ++++---
- 4 files changed, 43 insertions(+), 32 deletions(-)
-
-diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py
-index f5b9fd5b01..c33a356a75 100644
---- a/sos/policies/distros/__init__.py
-+++ b/sos/policies/distros/__init__.py
-@@ -29,6 +29,10 @@
- except ImportError:
-     REQUESTS_LOADED = False
- 
-+# Container environment variables for detecting if we're in a container
-+ENV_CONTAINER = 'container'
-+ENV_HOST_SYSROOT = 'HOST'
-+
- 
- class LinuxPolicy(Policy):
-     """This policy is meant to be an abc class that provides common
-@@ -69,10 +73,17 @@ def __init__(self, sysroot=None, init=None, probe_runtime=True):
-                                           probe_runtime=probe_runtime)
-         self.init_kernel_modules()
- 
-+        # need to set _host_sysroot before PackageManager()
-+        if sysroot:
-+            self._container_init()
-+            self._host_sysroot = sysroot
-+        else:
-+            sysroot = self._container_init()
-+
-         if init is not None:
-             self.init_system = init
-         elif os.path.isdir("/run/systemd/system/"):
--            self.init_system = SystemdInit()
-+            self.init_system = SystemdInit(chroot=sysroot)
-         else:
-             self.init_system = InitSystem()
- 
-@@ -130,6 +141,21 @@ def get_local_name(self):
-     def sanitize_filename(self, name):
-         return re.sub(r"[^-a-z,A-Z.0-9]", "", name)
- 
-+    def _container_init(self):
-+        """Check if sos is running in a container and perform container
-+        specific initialisation based on ENV_HOST_SYSROOT.
-+        """
-+        if ENV_CONTAINER in os.environ:
-+            if os.environ[ENV_CONTAINER] in ['docker', 'oci', 'podman']:
-+                self._in_container = True
-+        if ENV_HOST_SYSROOT in os.environ:
-+            self._host_sysroot = os.environ[ENV_HOST_SYSROOT]
-+        use_sysroot = self._in_container and self._host_sysroot is not None
-+        if use_sysroot:
-+            host_tmp_dir = os.path.abspath(self._host_sysroot + self._tmp_dir)
-+            self._tmp_dir = host_tmp_dir
-+        return self._host_sysroot if use_sysroot else None
-+
-     def init_kernel_modules(self):
-         """Obtain a list of loaded kernel modules to reference later for plugin
-         enablement and SoSPredicate checks
-diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py
-index b3a84336be..3476e21fb2 100644
---- a/sos/policies/distros/redhat.py
-+++ b/sos/policies/distros/redhat.py
-@@ -17,7 +17,7 @@
- from sos.presets.redhat import (RHEL_PRESETS, ATOMIC_PRESETS, RHV, RHEL,
-                                 CB, RHOSP, RHOCP, RH_CFME, RH_SATELLITE,
-                                 ATOMIC)
--from sos.policies.distros import LinuxPolicy
-+from sos.policies.distros import LinuxPolicy, ENV_HOST_SYSROOT
- from sos.policies.package_managers.rpm import RpmPackageManager
- from sos import _sos as _
- 
-@@ -56,12 +56,6 @@ def __init__(self, sysroot=None, init=None, probe_runtime=True,
-         super(RedHatPolicy, self).__init__(sysroot=sysroot, init=init,
-                                            probe_runtime=probe_runtime)
-         self.usrmove = False
--        # need to set _host_sysroot before PackageManager()
--        if sysroot:
--            self._container_init()
--            self._host_sysroot = sysroot
--        else:
--            sysroot = self._container_init()
- 
-         self.package_manager = RpmPackageManager(chroot=sysroot,
-                                                  remote_exec=remote_exec)
-@@ -140,21 +134,6 @@ def transform_path(path):
-         else:
-             return files
- 
--    def _container_init(self):
--        """Check if sos is running in a container and perform container
--        specific initialisation based on ENV_HOST_SYSROOT.
--        """
--        if ENV_CONTAINER in os.environ:
--            if os.environ[ENV_CONTAINER] in ['docker', 'oci', 'podman']:
--                self._in_container = True
--        if ENV_HOST_SYSROOT in os.environ:
--            self._host_sysroot = os.environ[ENV_HOST_SYSROOT]
--        use_sysroot = self._in_container and self._host_sysroot is not None
--        if use_sysroot:
--            host_tmp_dir = os.path.abspath(self._host_sysroot + self._tmp_dir)
--            self._tmp_dir = host_tmp_dir
--        return self._host_sysroot if use_sysroot else None
--
-     def runlevel_by_service(self, name):
-         from subprocess import Popen, PIPE
-         ret = []
-@@ -183,10 +162,6 @@ def get_tmp_dir(self, opt_tmp_dir):
-         return opt_tmp_dir
- 
- 
--# Container environment variables on Red Hat systems.
--ENV_CONTAINER = 'container'
--ENV_HOST_SYSROOT = 'HOST'
--
- # Legal disclaimer text for Red Hat products
- disclaimer_text = """
- Any information provided to %(vendor)s will be treated in \
-diff --git a/sos/policies/init_systems/__init__.py b/sos/policies/init_systems/__init__.py
-index dd663e6522..beac44cee3 100644
---- a/sos/policies/init_systems/__init__.py
-+++ b/sos/policies/init_systems/__init__.py
-@@ -29,9 +29,14 @@ class InitSystem():
-                       status of services
-     :type query_cmd: ``str``
- 
-+    :param chroot:  Location to chroot to for any command execution, i.e. the
-+                    sysroot if we're running in a container
-+    :type chroot:   ``str`` or ``None``
-+
-     """
- 
--    def __init__(self, init_cmd=None, list_cmd=None, query_cmd=None):
-+    def __init__(self, init_cmd=None, list_cmd=None, query_cmd=None,
-+                 chroot=None):
-         """Initialize a new InitSystem()"""
- 
-         self.services = {}
-@@ -39,6 +44,7 @@ def __init__(self, init_cmd=None, list_cmd=None, query_cmd=None):
-         self.init_cmd = init_cmd
-         self.list_cmd = "%s %s" % (self.init_cmd, list_cmd) or None
-         self.query_cmd = "%s %s" % (self.init_cmd, query_cmd) or None
-+        self.chroot = chroot
- 
-     def is_enabled(self, name):
-         """Check if given service name is enabled
-@@ -108,7 +114,10 @@ def _query_service(self, name):
-         """Query an individual service"""
-         if self.query_cmd:
-             try:
--                return sos_get_command_output("%s %s" % (self.query_cmd, name))
-+                return sos_get_command_output(
-+                    "%s %s" % (self.query_cmd, name),
-+                    chroot=self.chroot
-+                )
-             except Exception:
-                 return None
-         return None
-diff --git a/sos/policies/init_systems/systemd.py b/sos/policies/init_systems/systemd.py
-index 1b138f97b3..76dc57e27f 100644
---- a/sos/policies/init_systems/systemd.py
-+++ b/sos/policies/init_systems/systemd.py
-@@ -15,11 +15,12 @@
- class SystemdInit(InitSystem):
-     """InitSystem abstraction for SystemD systems"""
- 
--    def __init__(self):
-+    def __init__(self, chroot=None):
-         super(SystemdInit, self).__init__(
-             init_cmd='systemctl',
-             list_cmd='list-unit-files --type=service',
--            query_cmd='status'
-+            query_cmd='status',
-+            chroot=chroot
-         )
-         self.load_all_services()
- 
-@@ -30,7 +31,7 @@ def parse_query(self, output):
-         return 'unknown'
- 
-     def load_all_services(self):
--        svcs = shell_out(self.list_cmd).splitlines()[1:]
-+        svcs = shell_out(self.list_cmd, chroot=self.chroot).splitlines()[1:]
-         for line in svcs:
-             try:
-                 name = line.split('.service')[0]
--- 
-2.31.1
-
-From 9596473d1779b9c48e9923c220aaf2b8d9b3bebf Mon Sep 17 00:00:00 2001
-From: Jake Hunsaker <jhunsake@redhat.com>
-Date: Thu, 18 Nov 2021 13:17:14 -0500
-Subject: [PATCH] [global] Align sysroot determination and usage across sos
-
-The determination of sysroot - being automatic, user-specified, or
-controlled via environment variables in a container - has gotten muddied
-over time. This has resulted in different parts of the project;
-`Policy`, `Plugin`, `SoSComponent`, etc... to not always be in sync when
-sysroot is not `/`, thus causing varying and unexpected/unintended
-behavior.
-
-Fix this by only determining sysroot within `Policy()` initialization,
-and then using that determination across all aspects of the project that
-use or reference sysroot.
-
-This results in several changes:
-
-- `PackageManager()` will now (again) correctly reference host package
-  lists when sos is run in a container.
-
-- `ContainerRuntime()` is now able to activate when sos is running in a
-  container.
-
-- Plugins will now properly use sysroot for _all_ plugin enablement
-  triggers.
-
-- Plugins, Policy, and SoSComponents now all reference the
-  `self.sysroot` variable, rather than changing between `sysroot`.
-`_host_sysroot`, and `commons['sysroot']`. `_host_sysroot` has been
-removed from `Policy`.
-
-Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
----
- sos/archive.py                    |  2 +-
- sos/component.py                  |  2 +-
- sos/policies/__init__.py          | 11 +----------
- sos/policies/distros/__init__.py  | 33 +++++++++++++++++++------------
- sos/policies/distros/debian.py    |  2 +-
- sos/policies/distros/redhat.py    |  3 +--
- sos/policies/runtimes/__init__.py | 15 +++++++++-----
- sos/policies/runtimes/docker.py   |  4 ++--
- sos/report/__init__.py            |  6 ++----
- sos/report/plugins/__init__.py    | 22 +++++++++++----------
- sos/report/plugins/unpackaged.py  |  7 ++++---
- sos/utilities.py                  | 13 ++++++++----
- 12 files changed, 64 insertions(+), 56 deletions(-)
-
-diff --git a/sos/archive.py b/sos/archive.py
-index b02b2475..e3c68b77 100644
---- a/sos/archive.py
-+++ b/sos/archive.py
-@@ -153,7 +153,7 @@ class FileCacheArchive(Archive):
-         return (os.path.join(self._archive_root, name))
- 
-     def join_sysroot(self, path):
--        if path.startswith(self.sysroot):
-+        if not self.sysroot or path.startswith(self.sysroot):
-             return path
-         if path[0] == os.sep:
-             path = path[1:]
-diff --git a/sos/component.py b/sos/component.py
-index 5ac6e47f..dba0aabf 100644
---- a/sos/component.py
-+++ b/sos/component.py
-@@ -109,7 +109,7 @@ class SoSComponent():
-             try:
-                 import sos.policies
-                 self.policy = sos.policies.load(sysroot=self.opts.sysroot)
--                self.sysroot = self.policy.host_sysroot()
-+                self.sysroot = self.policy.sysroot
-             except KeyboardInterrupt:
-                 self._exit(0)
-             self._is_root = self.policy.is_root()
-diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py
-index fb8db1d7..ef9188de 100644
---- a/sos/policies/__init__.py
-+++ b/sos/policies/__init__.py
-@@ -110,7 +110,6 @@ any third party.
-     presets = {"": PresetDefaults()}
-     presets_path = PRESETS_PATH
-     _in_container = False
--    _host_sysroot = '/'
- 
-     def __init__(self, sysroot=None, probe_runtime=True):
-         """Subclasses that choose to override this initializer should call
-@@ -124,7 +123,7 @@ any third party.
-         self.package_manager = PackageManager()
-         self.valid_subclasses = [IndependentPlugin]
-         self.set_exec_path()
--        self._host_sysroot = sysroot
-+        self.sysroot = sysroot
-         self.register_presets(GENERIC_PRESETS)
- 
-     def check(self, remote=''):
-@@ -177,14 +176,6 @@ any third party.
-         """
-         return self._in_container
- 
--    def host_sysroot(self):
--        """Get the host's default sysroot
--
--        :returns: Host sysroot
--        :rtype: ``str`` or ``None``
--        """
--        return self._host_sysroot
--
-     def dist_version(self):
-         """
-         Return the OS version
-diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py
-index 7bdc81b8..c69fc1e7 100644
---- a/sos/policies/distros/__init__.py
-+++ b/sos/policies/distros/__init__.py
-@@ -71,19 +71,18 @@ class LinuxPolicy(Policy):
-     def __init__(self, sysroot=None, init=None, probe_runtime=True):
-         super(LinuxPolicy, self).__init__(sysroot=sysroot,
-                                           probe_runtime=probe_runtime)
--        self.init_kernel_modules()
- 
--        # need to set _host_sysroot before PackageManager()
-         if sysroot:
--            self._container_init()
--            self._host_sysroot = sysroot
-+            self.sysroot = sysroot
-         else:
--            sysroot = self._container_init()
-+            self.sysroot = self._container_init()
-+
-+        self.init_kernel_modules()
- 
-         if init is not None:
-             self.init_system = init
-         elif os.path.isdir("/run/systemd/system/"):
--            self.init_system = SystemdInit(chroot=sysroot)
-+            self.init_system = SystemdInit(chroot=self.sysroot)
-         else:
-             self.init_system = InitSystem()
- 
-@@ -149,27 +148,30 @@ class LinuxPolicy(Policy):
-             if os.environ[ENV_CONTAINER] in ['docker', 'oci', 'podman']:
-                 self._in_container = True
-         if ENV_HOST_SYSROOT in os.environ:
--            self._host_sysroot = os.environ[ENV_HOST_SYSROOT]
--        use_sysroot = self._in_container and self._host_sysroot is not None
-+            _host_sysroot = os.environ[ENV_HOST_SYSROOT]
-+        use_sysroot = self._in_container and _host_sysroot is not None
-         if use_sysroot:
--            host_tmp_dir = os.path.abspath(self._host_sysroot + self._tmp_dir)
-+            host_tmp_dir = os.path.abspath(_host_sysroot + self._tmp_dir)
-             self._tmp_dir = host_tmp_dir
--        return self._host_sysroot if use_sysroot else None
-+        return _host_sysroot if use_sysroot else None
- 
-     def init_kernel_modules(self):
-         """Obtain a list of loaded kernel modules to reference later for plugin
-         enablement and SoSPredicate checks
-         """
-         self.kernel_mods = []
-+        release = os.uname().release
- 
-         # first load modules from lsmod
--        lines = shell_out("lsmod", timeout=0).splitlines()
-+        lines = shell_out("lsmod", timeout=0, chroot=self.sysroot).splitlines()
-         self.kernel_mods.extend([
-             line.split()[0].strip() for line in lines[1:]
-         ])
- 
-         # next, include kernel builtins
--        builtins = "/usr/lib/modules/%s/modules.builtin" % os.uname().release
-+        builtins = self.join_sysroot(
-+            "/usr/lib/modules/%s/modules.builtin" % release
-+        )
-         try:
-             with open(builtins, "r") as mfile:
-                 for line in mfile:
-@@ -186,7 +188,7 @@ class LinuxPolicy(Policy):
-             'dm_mod': 'CONFIG_BLK_DEV_DM'
-         }
- 
--        booted_config = "/boot/config-%s" % os.uname().release
-+        booted_config = self.join_sysroot("/boot/config-%s" % release)
-         kconfigs = []
-         try:
-             with open(booted_config, "r") as kfile:
-@@ -200,6 +202,11 @@ class LinuxPolicy(Policy):
-             if config_strings[builtin] in kconfigs:
-                 self.kernel_mods.append(builtin)
- 
-+    def join_sysroot(self, path):
-+        if self.sysroot and self.sysroot != '/':
-+            path = os.path.join(self.sysroot, path.lstrip('/'))
-+        return path
-+
-     def pre_work(self):
-         # this method will be called before the gathering begins
- 
-diff --git a/sos/policies/distros/debian.py b/sos/policies/distros/debian.py
-index 95b389a6..639fd5eb 100644
---- a/sos/policies/distros/debian.py
-+++ b/sos/policies/distros/debian.py
-@@ -27,7 +27,7 @@ class DebianPolicy(LinuxPolicy):
-                  remote_exec=None):
-         super(DebianPolicy, self).__init__(sysroot=sysroot, init=init,
-                                            probe_runtime=probe_runtime)
--        self.package_manager = DpkgPackageManager(chroot=sysroot,
-+        self.package_manager = DpkgPackageManager(chroot=self.sysroot,
-                                                   remote_exec=remote_exec)
-         self.valid_subclasses += [DebianPlugin]
- 
-diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py
-index eb442407..4b14abaf 100644
---- a/sos/policies/distros/redhat.py
-+++ b/sos/policies/distros/redhat.py
-@@ -42,7 +42,6 @@ class RedHatPolicy(LinuxPolicy):
-     _redhat_release = '/etc/redhat-release'
-     _tmp_dir = "/var/tmp"
-     _in_container = False
--    _host_sysroot = '/'
-     default_scl_prefix = '/opt/rh'
-     name_pattern = 'friendly'
-     upload_url = None
-@@ -57,7 +56,7 @@ class RedHatPolicy(LinuxPolicy):
-                                            probe_runtime=probe_runtime)
-         self.usrmove = False
- 
--        self.package_manager = RpmPackageManager(chroot=sysroot,
-+        self.package_manager = RpmPackageManager(chroot=self.sysroot,
-                                                  remote_exec=remote_exec)
- 
-         self.valid_subclasses += [RedHatPlugin]
-diff --git a/sos/policies/runtimes/__init__.py b/sos/policies/runtimes/__init__.py
-index f28d6a1d..2e60ad23 100644
---- a/sos/policies/runtimes/__init__.py
-+++ b/sos/policies/runtimes/__init__.py
-@@ -64,7 +64,7 @@ class ContainerRuntime():
-         :returns: ``True`` if the runtime is active, else ``False``
-         :rtype: ``bool``
-         """
--        if is_executable(self.binary):
-+        if is_executable(self.binary, self.policy.sysroot):
-             self.active = True
-             return True
-         return False
-@@ -78,7 +78,7 @@ class ContainerRuntime():
-         containers = []
-         _cmd = "%s ps %s" % (self.binary, '-a' if get_all else '')
-         if self.active:
--            out = sos_get_command_output(_cmd)
-+            out = sos_get_command_output(_cmd, chroot=self.policy.sysroot)
-             if out['status'] == 0:
-                 for ent in out['output'].splitlines()[1:]:
-                     ent = ent.split()
-@@ -112,8 +112,10 @@ class ContainerRuntime():
-         images = []
-         fmt = '{{lower .Repository}}:{{lower .Tag}} {{lower .ID}}'
-         if self.active:
--            out = sos_get_command_output("%s images --format '%s'"
--                                         % (self.binary, fmt))
-+            out = sos_get_command_output(
-+                "%s images --format '%s'" % (self.binary, fmt),
-+                chroot=self.policy.sysroot
-+            )
-             if out['status'] == 0:
-                 for ent in out['output'].splitlines():
-                     ent = ent.split()
-@@ -129,7 +131,10 @@ class ContainerRuntime():
-         """
-         vols = []
-         if self.active:
--            out = sos_get_command_output("%s volume ls" % self.binary)
-+            out = sos_get_command_output(
-+                "%s volume ls" % self.binary,
-+                chroot=self.policy.sysroot
-+            )
-             if out['status'] == 0:
-                 for ent in out['output'].splitlines()[1:]:
-                     ent = ent.split()
-diff --git a/sos/policies/runtimes/docker.py b/sos/policies/runtimes/docker.py
-index 759dfaf6..e81f580e 100644
---- a/sos/policies/runtimes/docker.py
-+++ b/sos/policies/runtimes/docker.py
-@@ -18,9 +18,9 @@ class DockerContainerRuntime(ContainerRuntime):
-     name = 'docker'
-     binary = 'docker'
- 
--    def check_is_active(self):
-+    def check_is_active(self, sysroot=None):
-         # the daemon must be running
--        if (is_executable('docker') and
-+        if (is_executable('docker', sysroot) and
-                 (self.policy.init_system.is_running('docker') or
-                  self.policy.init_system.is_running('snap.docker.dockerd'))):
-             self.active = True
-diff --git a/sos/report/__init__.py b/sos/report/__init__.py
-index a4c92acc..a6c72778 100644
---- a/sos/report/__init__.py
-+++ b/sos/report/__init__.py
-@@ -173,14 +173,12 @@ class SoSReport(SoSComponent):
-         self._set_directories()
- 
-         msg = "default"
--        host_sysroot = self.policy.host_sysroot()
-+        self.sysroot = self.policy.sysroot
-         # set alternate system root directory
-         if self.opts.sysroot:
-             msg = "cmdline"
--            self.sysroot = self.opts.sysroot
--        elif self.policy.in_container() and host_sysroot != os.sep:
-+        elif self.policy.in_container() and self.sysroot != os.sep:
-             msg = "policy"
--            self.sysroot = host_sysroot
-         self.soslog.debug("set sysroot to '%s' (%s)" % (self.sysroot, msg))
- 
-         if self.opts.chroot not in chroot_modes:
-diff --git a/sos/report/plugins/__init__.py b/sos/report/plugins/__init__.py
-index 46028bb1..e180ae17 100644
---- a/sos/report/plugins/__init__.py
-+++ b/sos/report/plugins/__init__.py
-@@ -724,7 +724,7 @@ class Plugin():
-         """
-         if not self.use_sysroot():
-             return path
--        if path.startswith(self.sysroot):
-+        if self.sysroot and path.startswith(self.sysroot):
-             return path[len(self.sysroot):]
-         return path
- 
-@@ -743,8 +743,10 @@ class Plugin():
-                   ``False``
-         :rtype: ``bool``
-         """
--        paths = [self.sysroot, self.archive.get_tmp_dir()]
--        return os.path.commonprefix(paths) == self.sysroot
-+        # if sysroot is still None, that implies '/'
-+        _sysroot = self.sysroot or '/'
-+        paths = [_sysroot, self.archive.get_tmp_dir()]
-+        return os.path.commonprefix(paths) == _sysroot
- 
-     def is_installed(self, package_name):
-         """Is the package $package_name installed?
-@@ -2621,7 +2623,7 @@ class Plugin():
-         return list(set(expanded))
- 
-     def _collect_copy_specs(self):
--        for path in self.copy_paths:
-+        for path in sorted(self.copy_paths, reverse=True):
-             self._log_info("collecting path '%s'" % path)
-             self._do_copy_path(path)
-         self.generate_copyspec_tags()
-@@ -2749,7 +2751,7 @@ class Plugin():
- 
-         return ((any(self.path_exists(fname) for fname in files) or
-                 any(self.is_installed(pkg) for pkg in packages) or
--                any(is_executable(cmd) for cmd in commands) or
-+                any(is_executable(cmd, self.sysroot) for cmd in commands) or
-                 any(self.is_module_loaded(mod) for mod in self.kernel_mods) or
-                 any(self.is_service(svc) for svc in services) or
-                 any(self.container_exists(cntr) for cntr in containers)) and
-@@ -2817,7 +2819,7 @@ class Plugin():
-         :returns:           True if the path exists in sysroot, else False
-         :rtype:             ``bool``
-         """
--        return path_exists(path, self.commons['cmdlineopts'].sysroot)
-+        return path_exists(path, self.sysroot)
- 
-     def path_isdir(self, path):
-         """Helper to call the sos.utilities wrapper that allows the
-@@ -2830,7 +2832,7 @@ class Plugin():
-         :returns:           True if the path is a dir, else False
-         :rtype:             ``bool``
-         """
--        return path_isdir(path, self.commons['cmdlineopts'].sysroot)
-+        return path_isdir(path, self.sysroot)
- 
-     def path_isfile(self, path):
-         """Helper to call the sos.utilities wrapper that allows the
-@@ -2843,7 +2845,7 @@ class Plugin():
-         :returns:           True if the path is a file, else False
-         :rtype:             ``bool``
-         """
--        return path_isfile(path, self.commons['cmdlineopts'].sysroot)
-+        return path_isfile(path, self.sysroot)
- 
-     def path_islink(self, path):
-         """Helper to call the sos.utilities wrapper that allows the
-@@ -2856,7 +2858,7 @@ class Plugin():
-         :returns:           True if the path is a link, else False
-         :rtype:             ``bool``
-         """
--        return path_islink(path, self.commons['cmdlineopts'].sysroot)
-+        return path_islink(path, self.sysroot)
- 
-     def listdir(self, path):
-         """Helper to call the sos.utilities wrapper that allows the
-@@ -2869,7 +2871,7 @@ class Plugin():
-         :returns:           Contents of path, if it is a directory
-         :rtype:             ``list``
-         """
--        return listdir(path, self.commons['cmdlineopts'].sysroot)
-+        return listdir(path, self.sysroot)
- 
-     def path_join(self, path, *p):
-         """Helper to call the sos.utilities wrapper that allows the
-diff --git a/sos/report/plugins/unpackaged.py b/sos/report/plugins/unpackaged.py
-index 772b1d1f..24203c4b 100644
---- a/sos/report/plugins/unpackaged.py
-+++ b/sos/report/plugins/unpackaged.py
-@@ -58,10 +58,11 @@ class Unpackaged(Plugin, RedHatPlugin):
-             """
-             expanded = []
-             for f in files:
--                if self.path_islink(f):
--                    expanded.append("{} -> {}".format(f, os.readlink(f)))
-+                fp = self.path_join(f)
-+                if self.path_islink(fp):
-+                    expanded.append("{} -> {}".format(fp, os.readlink(fp)))
-                 else:
--                    expanded.append(f)
-+                    expanded.append(fp)
-             return expanded
- 
-         # Check command predicate to avoid costly processing
-diff --git a/sos/utilities.py b/sos/utilities.py
-index b7575153..d6630933 100644
---- a/sos/utilities.py
-+++ b/sos/utilities.py
-@@ -96,11 +96,15 @@ def grep(pattern, *files_or_paths):
-     return matches
- 
- 
--def is_executable(command):
-+def is_executable(command, sysroot=None):
-     """Returns if a command matches an executable on the PATH"""
- 
-     paths = os.environ.get("PATH", "").split(os.path.pathsep)
-     candidates = [command] + [os.path.join(p, command) for p in paths]
-+    if sysroot:
-+        candidates += [
-+            os.path.join(sysroot, c.lstrip('/')) for c in candidates
-+        ]
-     return any(os.access(path, os.X_OK) for path in candidates)
- 
- 
-@@ -216,8 +220,9 @@ def get_human_readable(size, precision=2):
- 
- 
- def _os_wrapper(path, sysroot, method, module=os.path):
--    if sysroot not in [None, '/']:
--        path = os.path.join(sysroot, path.lstrip('/'))
-+    if sysroot and sysroot != os.sep:
-+        if not path.startswith(sysroot):
-+            path = os.path.join(sysroot, path.lstrip('/'))
-     _meth = getattr(module, method)
-     return _meth(path)
- 
-@@ -243,7 +248,7 @@ def listdir(path, sysroot):
- 
- 
- def path_join(path, *p, sysroot=os.sep):
--    if not path.startswith(sysroot):
-+    if sysroot and not path.startswith(sysroot):
-         path = os.path.join(sysroot, path.lstrip(os.sep))
-     return os.path.join(path, *p)
- 
--- 
-2.31.1
-
 From 8bf602108f75db10e449eff5e2266c6466504086 Mon Sep 17 00:00:00 2001
 From: Nadia Pinaeva <npinaeva@redhat.com>
 Date: Thu, 2 Dec 2021 16:30:44 +0100
@@ -5868,20 +5024,122 @@ index cb20772fd..b59eade9a 100644
  
      def test_ip_parser_valid_ipv4_line(self):
          line = 'foobar foo 10.0.0.1/24 barfoo bar'
-From: Pavel Moravec <pmoravec@redhat.com>
-Subject: downstream-only patch to allow container_runtime change on 4.2
-sos cluster/collector already, as any 4.2 released version will support
-it.
-diff -rup a/sos/collector/sosnode.py b/sos/collector/sosnode.py
+From 2ae16e0245e1b01b8547e507abb69c11871a8467 Mon Sep 17 00:00:00 2001
+From: Jake Hunsaker <jhunsake@redhat.com>
+Date: Mon, 21 Feb 2022 14:37:09 -0500
+Subject: [PATCH] [sosnode] Handle downstream versioning for runtime option
+ check
+
+First, adds parsing and formatting for an sos installation's release
+version according to the loaded package manager for that node.
+
+Adds a fallback version check for 4.2-13 for RHEL downstreams that
+backport the `container-runtime` option into sos-4.2.
+
+Carry this in upstream to account for use cases where a workstation used
+to run `collect` from may be from a different stream than those used by
+cluster nodes.
+
+Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
+---
+ sos/collector/sosnode.py | 60 ++++++++++++++++++++++++++++++++++------
+ 1 file changed, 51 insertions(+), 9 deletions(-)
+
+diff --git a/sos/collector/sosnode.py b/sos/collector/sosnode.py
+index 7bbe0cd1..d9b998b0 100644
 --- a/sos/collector/sosnode.py
 +++ b/sos/collector/sosnode.py
-@@ -586,8 +586,6 @@ class SosNode():
-             if self.opts.cmd_timeout:
+@@ -275,21 +275,34 @@ class SosNode():
+     def _load_sos_info(self):
+         """Queries the node for information about the installed version of sos
+         """
++        ver = None
++        rel = None
+         if self.host.container_version_command is None:
+             pkg = self.host.package_manager.pkg_version(self.host.sos_pkg_name)
+             if pkg is not None:
+                 ver = '.'.join(pkg['version'])
+-                self.sos_info['version'] = ver
++                if pkg['release']:
++                    rel = pkg['release']
++
+         else:
+             # use the containerized policy's command
+             pkgs = self.run_command(self.host.container_version_command,
+                                     use_container=True, need_root=True)
+             if pkgs['status'] == 0:
+-                ver = pkgs['output'].strip().split('-')[1]
+-                if ver:
+-                    self.sos_info['version'] = ver
+-            else:
+-                self.sos_info['version'] = None
++                _, ver, rel = pkgs['output'].strip().split('-')
++
++        if ver:
++            if len(ver.split('.')) == 2:
++                # safeguard against maintenance releases throwing off the
++                # comparison by LooseVersion
++                ver += '.0'
++            try:
++                ver += '-%s' % rel.split('.')[0]
++            except Exception as err:
++                self.log_debug("Unable to fully parse sos release: %s" % err)
++
++        self.sos_info['version'] = ver
++
+         if self.sos_info['version']:
+             self.log_info('sos version is %s' % self.sos_info['version'])
+         else:
+@@ -381,9 +394,37 @@ class SosNode():
+         """Checks to see if the sos installation on the node is AT LEAST the
+         given ver. This means that if the installed version is greater than
+         ver, this will still return True
++
++        :param ver: Version number we are trying to verify is installed
++        :type ver:  ``str``
++
++        :returns:   True if installed version is at least ``ver``, else False
++        :rtype:     ``bool``
+         """
+-        return self.sos_info['version'] is not None and \
+-            LooseVersion(self.sos_info['version']) >= ver
++        def _format_version(ver):
++            # format the version we're checking to a standard form of X.Y.Z-R
++            try:
++                _fver = ver.split('-')[0]
++                _rel = ''
++                if '-' in ver:
++                    _rel = '-' + ver.split('-')[-1].split('.')[0]
++                if len(_fver.split('.')) == 2:
++                    _fver += '.0'
++
++                return _fver + _rel
++            except Exception as err:
++                self.log_debug("Unable to format '%s': %s" % (ver, err))
++                return ver
++
++        _ver = _format_version(ver)
++
++        try:
++            _node_ver = LooseVersion(self.sos_info['version'])
++            _test_ver = LooseVersion(_ver)
++            return _node_ver >= _test_ver
++        except Exception as err:
++            self.log_error("Error checking sos version: %s" % err)
++            return False
+ 
+     def is_installed(self, pkg):
+         """Checks if a given package is installed on the node"""
+@@ -587,7 +628,8 @@ class SosNode():
                  sos_opts.append('--cmd-timeout=%s'
                                  % quote(str(self.opts.cmd_timeout)))
--
+ 
 -        if self.check_sos_version('4.3'):
++        # handle downstream versions that backported this option
++        if self.check_sos_version('4.3') or self.check_sos_version('4.2-13'):
              if self.opts.container_runtime != 'auto':
                  sos_opts.append(
                      "--container-runtime=%s" % self.opts.container_runtime
+-- 
+2.34.1
 
diff --git a/SOURCES/sos-bz2043488-ovn-proper-package-enablement.patch b/SOURCES/sos-bz2043488-ovn-proper-package-enablement.patch
index 945eda9..16c48c4 100644
--- a/SOURCES/sos-bz2043488-ovn-proper-package-enablement.patch
+++ b/SOURCES/sos-bz2043488-ovn-proper-package-enablement.patch
@@ -43,3 +43,210 @@ index 78604a15a..25c38cccc 100644
  
  
  class DebianOVNHost(OVNHost, DebianPlugin, UbuntuPlugin):
+From 21fc376d97a5f74743e2b7cf7069349e874b979e Mon Sep 17 00:00:00 2001
+From: Hemanth Nakkina <hemanth.nakkina@canonical.com>
+Date: Fri, 4 Feb 2022 07:57:59 +0530
+Subject: [PATCH] [ovn-central] collect NB/SB ovsdb-server cluster status
+
+Add commands to collect cluster status of Northbound and
+Southbound ovsdb servers.
+
+Resolves: #2840
+
+Signed-off-by: Hemanth Nakkina hemanth.nakkina@canonical.com
+---
+ sos/report/plugins/ovn_central.py | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/sos/report/plugins/ovn_central.py b/sos/report/plugins/ovn_central.py
+index 0f947d4c5..2f0438df3 100644
+--- a/sos/report/plugins/ovn_central.py
++++ b/sos/report/plugins/ovn_central.py
+@@ -84,6 +84,14 @@ def setup(self):
+         else:
+             self.add_copy_spec("/var/log/ovn/*.log")
+ 
++        # ovsdb nb/sb cluster status commands
++        ovsdb_cmds = [
++            'ovs-appctl -t {} cluster/status OVN_Northbound'.format(
++                self.ovn_nbdb_sock_path),
++            'ovs-appctl -t {} cluster/status OVN_Southbound'.format(
++                self.ovn_sbdb_sock_path),
++        ]
++
+         # Some user-friendly versions of DB output
+         nbctl_cmds = [
+             'ovn-nbctl show',
+@@ -109,7 +117,8 @@ def setup(self):
+ 
+         self.add_database_output(nb_tables, nbctl_cmds, 'ovn-nbctl')
+ 
+-        cmds = nbctl_cmds
++        cmds = ovsdb_cmds
++        cmds += nbctl_cmds
+ 
+         # Can only run sbdb commands if we are the leader
+         co = {'cmd': "ovs-appctl -t {} cluster/status OVN_Southbound".
+@@ -148,10 +157,12 @@ def setup(self):
+ class RedHatOVNCentral(OVNCentral, RedHatPlugin):
+ 
+     packages = ('openvswitch-ovn-central', 'ovn.*-central', )
++    ovn_nbdb_sock_path = '/var/run/openvswitch/ovnnb_db.ctl'
+     ovn_sbdb_sock_path = '/var/run/openvswitch/ovnsb_db.ctl'
+ 
+ 
+ class DebianOVNCentral(OVNCentral, DebianPlugin, UbuntuPlugin):
+ 
+     packages = ('ovn-central', )
++    ovn_nbdb_sock_path = '/var/run/ovn/ovnnb_db.ctl'
+     ovn_sbdb_sock_path = '/var/run/ovn/ovnsb_db.ctl'
+From d0f9d507b0ec63c9e8f3e5d7b6507d9d0f97c038 Mon Sep 17 00:00:00 2001
+From: Jake Hunsaker <jhunsake@redhat.com>
+Date: Tue, 15 Feb 2022 16:24:47 -0500
+Subject: [PATCH] [runtimes] Allow container IDs to be used with
+ `container_exists()`
+
+As container runtimes can interchange container names and container IDs,
+sos should also allow the use of container IDs when checking for the
+presence of a given container.
+
+In particular, this change unblocks the use of `Plugin.exec_cmd()` when
+used in conjunction with `Plugin.get_container_by_name()` to pick a
+container based on a provided regex that the container name may match.
+
+Related: #2856
+
+Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
+---
+ sos/policies/runtimes/__init__.py | 17 +++++++++++++++++
+ sos/report/plugins/__init__.py    |  6 +++---
+ 2 files changed, 20 insertions(+), 3 deletions(-)
+
+diff --git a/sos/policies/runtimes/__init__.py b/sos/policies/runtimes/__init__.py
+index 5ac673544..d28373496 100644
+--- a/sos/policies/runtimes/__init__.py
++++ b/sos/policies/runtimes/__init__.py
+@@ -147,6 +147,23 @@ def get_volumes(self):
+                     vols.append(ent[-1])
+         return vols
+ 
++    def container_exists(self, container):
++        """Check if a given container ID or name exists on the system from the
++        perspective of the container runtime.
++
++        Note that this will only check _running_ containers
++
++        :param container:       The name or ID of the container
++        :type container:        ``str``
++
++        :returns:               True if the container exists, else False
++        :rtype:                 ``bool``
++        """
++        for _contup in self.containers:
++            if container in _contup:
++                return True
++        return False
++
+     def fmt_container_cmd(self, container, cmd, quotecmd):
+         """Format a command to run inside a container using the runtime
+ 
+diff --git a/sos/report/plugins/__init__.py b/sos/report/plugins/__init__.py
+index 2988be089..cc5cb65bc 100644
+--- a/sos/report/plugins/__init__.py
++++ b/sos/report/plugins/__init__.py
+@@ -2593,7 +2593,7 @@ def container_exists(self, name):
+         """If a container runtime is present, check to see if a container with
+         a given name is currently running
+ 
+-        :param name:    The name of the container to check presence of
++        :param name:    The name or ID of the container to check presence of
+         :type name: ``str``
+ 
+         :returns: ``True`` if `name` exists, else ``False``
+@@ -2601,8 +2601,8 @@ def container_exists(self, name):
+         """
+         _runtime = self._get_container_runtime()
+         if _runtime is not None:
+-            con = _runtime.get_container_by_name(name)
+-            return con is not None
++            return (_runtime.container_exists(name) or
++                    _runtime.get_container_by_name(name) is not None)
+         return False
+ 
+     def get_all_containers_by_regex(self, regex, get_all=False):
+
+From de9b020a72d1ceda39587db4c6d5acf72cd90da2 Mon Sep 17 00:00:00 2001
+From: Fernando Royo <froyo@redhat.com>
+Date: Tue, 15 Feb 2022 10:00:38 +0100
+Subject: [PATCH] [ovn_central] Rename container responsable of Red Hat
+ ovn_central plugin
+
+ovn_central plugin is running by container with
+name 'ovn-dbs-bundle*', a typo has been identified and
+this cause plugin ovn_central not enabled by default as it
+does not recognize any container responsible of this.
+
+This patch fix this container name match, searching schema db
+keeping backward compatibility with openvswitch.
+---
+ sos/report/plugins/ovn_central.py | 23 ++++++++++++-----------
+ 1 file changed, 12 insertions(+), 11 deletions(-)
+
+diff --git a/sos/report/plugins/ovn_central.py b/sos/report/plugins/ovn_central.py
+index 2f0438df..2f34bff0 100644
+--- a/sos/report/plugins/ovn_central.py
++++ b/sos/report/plugins/ovn_central.py
+@@ -24,7 +24,7 @@ class OVNCentral(Plugin):
+     short_desc = 'OVN Northd'
+     plugin_name = "ovn_central"
+     profiles = ('network', 'virt')
+-    containers = ('ovs-db-bundle.*',)
++    containers = ('ovn-dbs-bundle.*',)
+ 
+     def get_tables_from_schema(self, filename, skip=[]):
+         if self._container_name:
+@@ -66,7 +66,7 @@ class OVNCentral(Plugin):
+             cmds.append('%s list %s' % (ovn_cmd, table))
+ 
+     def setup(self):
+-        self._container_name = self.get_container_by_name('ovs-dbs-bundle.*')
++        self._container_name = self.get_container_by_name(self.containers[0])
+ 
+         ovs_rundir = os.environ.get('OVS_RUNDIR')
+         for pidfile in ['ovnnb_db.pid', 'ovnsb_db.pid', 'ovn-northd.pid']:
+@@ -110,12 +110,11 @@ class OVNCentral(Plugin):
+             'ovn-sbctl get-connection',
+         ]
+ 
+-        schema_dir = '/usr/share/openvswitch'
+-
+-        nb_tables = self.get_tables_from_schema(self.path_join(
+-            schema_dir, 'ovn-nb.ovsschema'))
+-
+-        self.add_database_output(nb_tables, nbctl_cmds, 'ovn-nbctl')
++        # backward compatibility
++        for path in ['/usr/share/openvswitch', '/usr/share/ovn']:
++            nb_tables = self.get_tables_from_schema(self.path_join(
++                path, 'ovn-nb.ovsschema'))
++            self.add_database_output(nb_tables, nbctl_cmds, 'ovn-nbctl')
+ 
+         cmds = ovsdb_cmds
+         cmds += nbctl_cmds
+@@ -125,9 +124,11 @@ class OVNCentral(Plugin):
+               format(self.ovn_sbdb_sock_path),
+               "output": "Leader: self"}
+         if self.test_predicate(self, pred=SoSPredicate(self, cmd_outputs=co)):
+-            sb_tables = self.get_tables_from_schema(self.path_join(
+-                schema_dir, 'ovn-sb.ovsschema'), ['Logical_Flow'])
+-            self.add_database_output(sb_tables, sbctl_cmds, 'ovn-sbctl')
++            # backward compatibility
++            for path in ['/usr/share/openvswitch', '/usr/share/ovn']:
++                sb_tables = self.get_tables_from_schema(self.path_join(
++                    path, 'ovn-sb.ovsschema'), ['Logical_Flow'])
++                self.add_database_output(sb_tables, sbctl_cmds, 'ovn-sbctl')
+             cmds += sbctl_cmds
+ 
+         # If OVN is containerized, we need to run the above commands inside
+-- 
+2.34.1
+
diff --git a/SOURCES/sos-bz2054883-plugopt-logging-effective-opts.patch b/SOURCES/sos-bz2054883-plugopt-logging-effective-opts.patch
new file mode 100644
index 0000000..f8e7ed3
--- /dev/null
+++ b/SOURCES/sos-bz2054883-plugopt-logging-effective-opts.patch
@@ -0,0 +1,94 @@
+From 5824cd5d3bddf39e0382d568419e2453abc93d8a Mon Sep 17 00:00:00 2001
+From: Jake Hunsaker <jhunsake@redhat.com>
+Date: Mon, 30 Aug 2021 15:09:07 -0400
+Subject: [PATCH] [options] Fix logging on plugopts in effective sos command
+
+First, provide a special-case handling for plugin options specified in
+sos.conf in `SoSOptions.to_args().has_value()` that allows for plugin
+options to be included in the "effective options now" log message.
+
+Second, move the logging of said message (and thus the merging of
+preset options, if used), to being _prior_ to the loading of plugin
+options.
+
+Combined, plugin options specified in sos.conf will now be logged
+properly and this logging will occur before we set (and log the setting
+of) those options.
+
+Resolves: #2663
+
+Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
+---
+ sos/options.py         |  2 ++
+ sos/report/__init__.py | 30 ++++++++++++++++--------------
+ 2 files changed, 18 insertions(+), 14 deletions(-)
+
+diff --git a/sos/options.py b/sos/options.py
+index a014a022..7bea3ffc 100644
+--- a/sos/options.py
++++ b/sos/options.py
+@@ -281,6 +281,8 @@ class SoSOptions():
+             null_values = ("False", "None", "[]", '""', "''", "0")
+             if not value or value in null_values:
+                 return False
++            if name == 'plugopts' and value:
++                return True
+             if name in self.arg_defaults:
+                 if str(value) == str(self.arg_defaults[name]):
+                     return False
+diff --git a/sos/report/__init__.py b/sos/report/__init__.py
+index b0159e5b..82484f1d 100644
+--- a/sos/report/__init__.py
++++ b/sos/report/__init__.py
+@@ -925,20 +925,6 @@ class SoSReport(SoSComponent):
+         self._exit(1)
+ 
+     def setup(self):
+-        # Log command line options
+-        msg = "[%s:%s] executing 'sos %s'"
+-        self.soslog.info(msg % (__name__, "setup", " ".join(self.cmdline)))
+-
+-        # Log active preset defaults
+-        preset_args = self.preset.opts.to_args()
+-        msg = ("[%s:%s] using '%s' preset defaults (%s)" %
+-               (__name__, "setup", self.preset.name, " ".join(preset_args)))
+-        self.soslog.info(msg)
+-
+-        # Log effective options after applying preset defaults
+-        self.soslog.info("[%s:%s] effective options now: %s" %
+-                         (__name__, "setup", " ".join(self.opts.to_args())))
+-
+         self.ui_log.info(_(" Setting up plugins ..."))
+         for plugname, plug in self.loaded_plugins:
+             try:
+@@ -1386,11 +1372,27 @@ class SoSReport(SoSComponent):
+         self.report_md.add_list('disabled_plugins', self.opts.skip_plugins)
+         self.report_md.add_section('plugins')
+ 
++    def _merge_preset_options(self):
++        # Log command line options
++        msg = "[%s:%s] executing 'sos %s'"
++        self.soslog.info(msg % (__name__, "setup", " ".join(self.cmdline)))
++
++        # Log active preset defaults
++        preset_args = self.preset.opts.to_args()
++        msg = ("[%s:%s] using '%s' preset defaults (%s)" %
++               (__name__, "setup", self.preset.name, " ".join(preset_args)))
++        self.soslog.info(msg)
++
++        # Log effective options after applying preset defaults
++        self.soslog.info("[%s:%s] effective options now: %s" %
++                         (__name__, "setup", " ".join(self.opts.to_args())))
++
+     def execute(self):
+         try:
+             self.policy.set_commons(self.get_commons())
+             self.load_plugins()
+             self._set_all_options()
++            self._merge_preset_options()
+             self._set_tunables()
+             self._check_for_unknown_plugins()
+             self._set_plugin_options()
+-- 
+2.34.1
+
diff --git a/SOURCES/sos-bz2055548-honour-plugins-timeout-hardcoded.patch b/SOURCES/sos-bz2055548-honour-plugins-timeout-hardcoded.patch
new file mode 100644
index 0000000..3adde40
--- /dev/null
+++ b/SOURCES/sos-bz2055548-honour-plugins-timeout-hardcoded.patch
@@ -0,0 +1,39 @@
+From 7069e99d1c5c443f96a98a7ed6db67fa14683e67 Mon Sep 17 00:00:00 2001
+From: Pavel Moravec <pmoravec@redhat.com>
+Date: Thu, 17 Feb 2022 09:14:15 +0100
+Subject: [PATCH] [report] Honor plugins' hardcoded plugin_timeout
+
+Currently, plugin's plugin_timeout hardcoded default is superseded by
+whatever --plugin-timeout value, even when this option is not used and
+we eval it to TIMEOUT_DEFAULT.
+
+In this case of not setting --plugin-timeout either -k plugin.timeout,
+honour plugin's plugin_timeout instead.
+
+Resolves: #2863
+Closes: #2864
+
+Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
+---
+ sos/report/plugins/__init__.py | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/sos/report/plugins/__init__.py b/sos/report/plugins/__init__.py
+index cc5cb65b..336b4d22 100644
+--- a/sos/report/plugins/__init__.py
++++ b/sos/report/plugins/__init__.py
+@@ -636,7 +636,10 @@ class Plugin():
+             if opt_timeout is None:
+                 _timeout = own_timeout
+             elif opt_timeout is not None and own_timeout == -1:
+-                _timeout = int(opt_timeout)
++                if opt_timeout == TIMEOUT_DEFAULT:
++                    _timeout = default_timeout
++                else:
++                    _timeout = int(opt_timeout)
+             elif opt_timeout is not None and own_timeout > -1:
+                 _timeout = own_timeout
+             else:
+-- 
+2.34.1
+
diff --git a/SPECS/sos.spec b/SPECS/sos.spec
index 5da5db9..e468c18 100644
--- a/SPECS/sos.spec
+++ b/SPECS/sos.spec
@@ -5,7 +5,7 @@
 Summary: A set of tools to gather troubleshooting information from a system
 Name: sos
 Version: 4.2
-Release: 13%{?dist}
+Release: 15%{?dist}
 Group: Applications/System
 Source0: https://github.com/sosreport/sos/archive/%{version}/sos-%{version}.tar.gz
 Source1: sos-audit-%{auditversion}.tgz
@@ -45,6 +45,8 @@ Patch18: sos-bz2037350-ocp-backports.patch
 Patch19: sos-bz2043104-foreman-tasks-msgpack.patch
 Patch20: sos-bz2041855-virsh-in-foreground.patch
 Patch21: sos-bz2043488-ovn-proper-package-enablement.patch
+Patch22: sos-bz2054883-plugopt-logging-effective-opts.patch
+Patch23: sos-bz2055548-honour-plugins-timeout-hardcoded.patch
 
 %description
 Sos is a set of tools that gathers information about system
@@ -76,6 +78,8 @@ support technicians and developers.
 %patch19 -p1
 %patch20 -p1
 %patch21 -p1
+%patch22 -p1
+%patch23 -p1
 
 %build
 %py3_build
@@ -143,6 +147,18 @@ of the system.  Currently storage and filesystem commands are audited.
 
 
 %changelog
+* Wed Feb 23 2022 Pavel Moravec <pmoravec@redhat.com> = 4.2-15
+- [sosnode] Handle downstream versioning for runtime option
+  Resolves: bz2037350
+- [options] Fix logging on plugopts in effective sos command
+  Resolves: bz2054883
+- [report] Honor plugins' hardcoded plugin_timeout
+  Resolves: bz2055548
+- [policies] Set fallback to None sysroot, don't chroot to '/'
+  Resolves: bz2011537
+- [ovn_central] Rename container responsable of Red Hat
+  Resolves: bz2043488
+
 * Wed Jan 26 2022 Pavel Moravec <pmoravec@redhat.com> = 4.2-13
 - [virsh] Catch parsing exception
   Resolves: bz2041855