image-info: Add workaround for listing services by status
`image-info` tools parses output of `systemctl list-unit-files` run on a different tree (with `--root` option), to determine the list of enabled and disabled services on the inspected image. However since Fedora 33 (and presumably since systemd v246), the output of `systemctl list-unit-files` changed. Some units previously reported as "enabled" or "disabled" are now reported as "alias", which means, that they are just a symlink to a different unit. There is no systemd command, that would take an "alias" unit and would report its state as "enabled" or "disabled" and could run on a different tree (with "--root" option). To make the list of reported services in the given state consistent on systems with older and new (v246+) systemd version, check all "alias" units and append them to the list of services with a specific status, if their target is also listed in in the list. Example of the `systemctl list-unit-files` output change: ~]# rpm -q systemd systemd-246.6-3.fc33.x86_64 ~]# systemctl list-unit-files ctrl-alt-del.target UNIT FILE STATE VENDOR PRESET ctrl-alt-del.target alias - ~]# rpm -q systemd systemd-245.8-2.fc32.x86_64 ~]# systemctl list-unit-files ctrl-alt-del.target UNIT FILE STATE VENDOR PRESET ctrl-alt-del.target enabled disabled This change makes it possible to produce consistent output for an inspected image, regardless if the `image-info` tool is run on Fedora 32, Fedora 33 or RHEL-8. Also regenerate all Fedora 33 test cases, since this commit changes the content of produced list of enabled / disabled services since Fedora 33. The list is now consistent with what would be produced by `image-info` for an image on older Fedora (e.g. 32) or RHEL-8. Signed-off-by: Tomas Hozza <thozza@redhat.com>
This commit is contained in:
parent
9b7fb4fb63
commit
1a3cbb282a
9 changed files with 137 additions and 8 deletions
|
|
@ -134,9 +134,6 @@ def parse_unit_files(s, expected_state):
|
|||
continue
|
||||
r.append(unit)
|
||||
|
||||
# deduplicate and sort
|
||||
r = list(set(r))
|
||||
r.sort()
|
||||
return r
|
||||
|
||||
|
||||
|
|
@ -265,8 +262,69 @@ def rpm_packages(tree, is_ostree):
|
|||
return list(sorted(pkgs))
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def change_root(root):
|
||||
real_root = os.open("/", os.O_RDONLY)
|
||||
try:
|
||||
os.chroot(root)
|
||||
yield None
|
||||
finally:
|
||||
os.fchdir(real_root)
|
||||
os.chroot(".")
|
||||
os.close(real_root)
|
||||
|
||||
|
||||
def read_services(tree, state):
|
||||
return subprocess_check_output(["systemctl", f"--root={tree}", "list-unit-files"], (lambda s: parse_unit_files(s, state)))
|
||||
services_state = subprocess_check_output(["systemctl", f"--root={tree}", "list-unit-files"], (lambda s: parse_unit_files(s, state)))
|
||||
|
||||
# Since systemd v246, some services previously reported as "enabled" /
|
||||
# "disabled" are now reported as "alias". There is no systemd command, that
|
||||
# would take an "alias" unit and report its state as enabled/disabled
|
||||
# and could run on a different tree (with "--root" option).
|
||||
# To make the produced list of services in the given state consistent on
|
||||
# pre/post v246 systemd versions, check all "alias" units and append them
|
||||
# to the list, if their target is also listed in 'services_state'.
|
||||
if state != "alias":
|
||||
services_alias = subprocess_check_output(["systemctl", f"--root={tree}", "list-unit-files"], (lambda s: parse_unit_files(s, "alias")))
|
||||
|
||||
for alias in services_alias:
|
||||
# The service may be in one of the following places (output of
|
||||
# "systemd-analyze unit-paths", it should not change too often).
|
||||
unit_paths = [
|
||||
"/etc/systemd/system.control",
|
||||
"/run/systemd/system.control",
|
||||
"/run/systemd/transient",
|
||||
"/run/systemd/generator.early",
|
||||
"/etc/systemd/system",
|
||||
"/run/systemd/system",
|
||||
"/run/systemd/generator",
|
||||
"/usr/local/lib/systemd/system",
|
||||
"/usr/lib/systemd/system",
|
||||
"/run/systemd/generator.late"
|
||||
]
|
||||
|
||||
with change_root(tree):
|
||||
for path in unit_paths:
|
||||
unit_path = os.path.join(path, alias)
|
||||
if os.path.exists(unit_path):
|
||||
real_unit_path = os.path.realpath(unit_path)
|
||||
# Skip the alias, if there was a symlink cycle.
|
||||
# When symbolic link cycles occur, the returned path will
|
||||
# be one member of the cycle, but no guarantee is made about
|
||||
# which member that will be.
|
||||
if os.path.islink(real_unit_path):
|
||||
continue
|
||||
|
||||
# Append the alias unit to the list, if its target is
|
||||
# already there.
|
||||
if os.path.basename(real_unit_path) in services_state:
|
||||
services_state.append(alias)
|
||||
|
||||
# deduplicate and sort
|
||||
services_state = list(set(services_state))
|
||||
services_state.sort()
|
||||
|
||||
return services_state
|
||||
|
||||
|
||||
def read_default_target(tree):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue