Skip to content

Commit 98cd1c3

Browse files
committed
fixup! add detection for custom libraries registered by ld.so.conf
1 parent 0f67e0f commit 98cd1c3

File tree

3 files changed

+101
-22
lines changed

3 files changed

+101
-22
lines changed

repos/system_upgrade/common/actors/checkldconf/actor.py renamed to repos/system_upgrade/common/actors/checkldsoconf/actor.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ class CheckLdSoConfiguration(Actor):
1010
1111
The ld.so configuration files are used to overwrite standard library links
1212
in order to use different custom libraries. The in-place upgrade does not
13-
support customization of this configuration by user. This actor inhibits the
14-
upgrade upon detecting such customization.
13+
support customization of this configuration by user. This actor creates high
14+
severity report upon detecting such customization.
1515
"""
1616

1717
name = 'check_ld_so_configuration'

repos/system_upgrade/common/actors/checkldconf/libraries/checkldsoconfiguration.py renamed to repos/system_upgrade/common/actors/checkldsoconf/libraries/checkldsoconfiguration.py

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
LD_SO_CONF_DIR = '/etc/ld.so.conf.d'
1010
LD_SO_CONF_MAIN = '/etc/ld.so.conf'
1111
LD_SO_CONF_DEFAULT_INCLUDE = 'ld.so.conf.d/*.conf'
12-
LIST_FORMAT_PREFIX = '\n - '
12+
LD_SO_CONF_COMMENT_PREFIX = '#'
13+
FMT_LIST_SEPARATOR = '\n - '
1314

1415

1516
def _read_file(file_path):
@@ -20,29 +21,57 @@ def _read_file(file_path):
2021
def _report_custom_ld_so_configuration(summary):
2122
reporting.create_report([
2223
reporting.Title(
23-
'Third party libraries linked with ld.so.conf are not supported for the in-place upgrade.'
24+
'Third party libraries linked with ld.so.conf are not supported.'
2425
),
2526
reporting.Summary(summary),
26-
reporting.Remediation('Remove the custom ld.so configuration.'),
27+
reporting.Remediation(hint=('Remove the custom ld.so configuration and apply the changes '
28+
'using the ldconfig command.')),
2729
reporting.RelatedResource('file', '/etc/ld.so.conf'),
2830
reporting.RelatedResource('directory', '/etc/ld.so.conf.d'),
2931
reporting.Severity(reporting.Severity.HIGH),
30-
reporting.Groups([reporting.Groups.OS_FACTS, reporting.Groups.INHIBITOR]),
32+
reporting.Groups([reporting.Groups.OS_FACTS]),
3133
])
3234

3335

36+
def _is_modified(config_path, package_name):
37+
""" Decide if the configuration file was modified based on the package it belongs to. """
38+
is_modified = False
39+
try:
40+
run(['rpm', '-Va', package_name])
41+
except CalledProcessError as err:
42+
modified_files = err.stdout
43+
for line in modified_files.split('\n'):
44+
line = line.split(' ')
45+
modification_flags, file_path = line[0].strip(), line[-1].strip()
46+
# Note that the condition is relaxed so that files different only
47+
# in the modification timestamp won't be considered as modified.
48+
is_modified |= config_path == file_path and modification_flags.strip('.') != 'T'
49+
return is_modified
50+
51+
3452
def _is_included_ld_so_config_custom(config_path):
3553
if not os.path.isfile(config_path):
3654
return False
3755

56+
# Check if the config file has any lines that have an effect on ld.so configuration
57+
has_effective_line = False
58+
for line in _read_file(config_path):
59+
line = line.strip()
60+
if line and not line.startswith(LD_SO_CONF_COMMENT_PREFIX):
61+
has_effective_line = True
62+
break
63+
64+
if not has_effective_line:
65+
# The config file has no effect on the ld.so configuration therefore we do not consider custom
66+
return False
67+
3868
is_custom = False
3969
try:
4070
package_name = run(['rpm', '-qf', '--queryformat', '%{NAME}', config_path])['stdout']
41-
is_custom = not has_package(InstalledRedHatSignedRPM, package_name)
71+
is_custom = not has_package(InstalledRedHatSignedRPM, package_name) or _is_modified(config_path, package_name)
4272
except CalledProcessError:
4373
is_custom = True
44-
api.current_logger().debug('The following config file is{}considered a custom '
45-
'ld.so configuration: {}'.format(' ' if is_custom else ' NOT ', config_path))
74+
4675
return is_custom
4776

4877

@@ -57,7 +86,7 @@ def _parse_main_ld_so_config():
5786
cfg_glob = line.split(' ', 1)[1].strip()
5887
cfg_glob = os.path.join('/etc', cfg_glob) if not os.path.isabs(cfg_glob) else cfg_glob
5988
included_configs.append(cfg_glob)
60-
elif line:
89+
elif line and not line.startswith(LD_SO_CONF_COMMENT_PREFIX):
6190
other_lines.append(line)
6291

6392
return included_configs, other_lines
@@ -70,8 +99,8 @@ def check_ld_so_configuration():
7099
if other_lines:
71100
# The main ld.so config file is expected to only include additional configs that are owned by a package
72101
summary += ('The /etc/ld.so.conf file has unexpected contents:'
73-
'{}{}'.format(LIST_FORMAT_PREFIX,
74-
LIST_FORMAT_PREFIX.join(other_lines)))
102+
'{}{}'.format(FMT_LIST_SEPARATOR,
103+
FMT_LIST_SEPARATOR.join(other_lines)))
75104

76105
is_default_include_present = '/etc/' + LD_SO_CONF_DEFAULT_INCLUDE in included_configs
77106
if not is_default_include_present:
@@ -91,8 +120,8 @@ def check_ld_so_configuration():
91120

92121
if custom_ld_configs:
93122
summary += ('\nThe following config files were marked as unsupported:'
94-
'{}{}'.format(LIST_FORMAT_PREFIX,
95-
LIST_FORMAT_PREFIX.join(custom_ld_configs)))
123+
'{}{}'.format(FMT_LIST_SEPARATOR,
124+
FMT_LIST_SEPARATOR.join(custom_ld_configs)))
96125

97126
if summary:
98127
_report_custom_ld_so_configuration(summary)

repos/system_upgrade/common/actors/checkldconf/tests/test_checkldsoconfiguration.py renamed to repos/system_upgrade/common/actors/checkldsoconf/tests/test_checkldsoconfiguration.py

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,12 @@ def test_check_ld_so_configuration(monkeypatch, included_configs_glob_dict, othe
7979
[
8080
(['include ld.so.conf.d/*.conf\n'],
8181
['/etc/ld.so.conf.d/*.conf'], []),
82-
(['include ld.so.conf.d/*.conf\n', '\n', '/custom/path.lib\n'],
82+
(['include ld.so.conf.d/*.conf\n', '\n', '/custom/path.lib\n', '#comment'],
8383
['/etc/ld.so.conf.d/*.conf'], ['/custom/path.lib']),
8484
(['include ld.so.conf.d/*.conf\n', 'include /custom/path.conf\n'],
8585
['/etc/ld.so.conf.d/*.conf', '/custom/path.conf'], []),
86+
(['include ld.so.conf.d/*.conf\n', '#include /custom/path.conf\n', '#/custom/path.conf\n'],
87+
['/etc/ld.so.conf.d/*.conf'], []),
8688
([' \n'],
8789
[], [])
8890
])
@@ -99,27 +101,75 @@ def mocked_read_file(path):
99101
assert _other_lines == other_lines
100102

101103

102-
@pytest.mark.parametrize(('config_path', 'run_result', 'package_exists', 'is_custom'),
104+
@pytest.mark.parametrize(('config_path', 'package_name', 'run_result', 'is_modified'),
103105
[
104-
('/etc/ld.so.conf.d/dyninst-x86_64.conf', 'dyninst', True, False),
105-
('/etc/ld.so.conf.d/somelib.conf', CalledProcessError, False, True),
106-
('/etc/custom/custom.conf', 'custom', False, True)
106+
('/etc/ld.so.conf.d/dyninst-x86_64.conf', 'dyninst',
107+
'.......T. c /etc/ld.so.conf.d/dyninst-x86_64.conf', False),
108+
('/etc/ld.so.conf.d/dyninst-x86_64.conf', 'dyninst',
109+
'S.5....T. c /etc/ld.so.conf.d/dyninst-x86_64.conf', True),
110+
('/etc/ld.so.conf.d/kernel-3.10.0-1160.el7.x86_64.conf', 'kernel',
111+
'', False)
107112
])
108-
def test_is_included_ld_so_config_custom(monkeypatch, config_path, run_result, package_exists, is_custom):
113+
def test_is_modified(monkeypatch, config_path, package_name, run_result, is_modified):
114+
def mocked_run(command):
115+
assert package_name in command
116+
if run_result:
117+
raise CalledProcessError("message", command, {'stdout': run_result})
118+
return ''
119+
120+
monkeypatch.setattr(checkldsoconfiguration, 'run', mocked_run)
121+
122+
_is_modified = checkldsoconfiguration._is_modified(config_path, package_name)
123+
assert _is_modified == is_modified
124+
125+
126+
@pytest.mark.parametrize(('config_path',
127+
'config_contents', 'run_result',
128+
'is_installed_rh_signed_package', 'is_modified', 'has_effective_lines'),
129+
[
130+
('/etc/ld.so.conf.d/dyninst-x86_64.conf',
131+
['/usr/lib64/dyninst\n'], 'dyninst',
132+
True, False, True), # RH sighend package without modification - Not custom
133+
('/etc/ld.so.conf.d/dyninst-x86_64.conf',
134+
['/usr/lib64/my_dyninst\n'], 'dyninst',
135+
True, True, True), # Was modified by user - Custom
136+
('/etc/custom/custom.conf',
137+
['/usr/lib64/custom'], 'custom',
138+
False, None, True), # Third-party package - Custom
139+
('/etc/custom/custom.conf',
140+
['#/usr/lib64/custom\n'], 'custom',
141+
False, None, False), # Third-party package without effective lines - Not custom
142+
('/etc/ld.so.conf.d/somelib.conf',
143+
['/usr/lib64/somelib\n'], CalledProcessError,
144+
None, None, True), # User created configuration file - Custom
145+
('/etc/ld.so.conf.d/somelib.conf',
146+
['#/usr/lib64/somelib\n'], CalledProcessError,
147+
None, None, False) # User created configuration file without effective lines - Not custom
148+
])
149+
def test_is_included_ld_so_config_custom(monkeypatch, config_path, config_contents, run_result,
150+
is_installed_rh_signed_package, is_modified, has_effective_lines):
109151
def mocked_run(command):
110152
assert config_path in command
111153
if run_result and not isinstance(run_result, str):
112-
raise CalledProcessError("message", ["command"], "result")
154+
raise CalledProcessError("message", command, "result")
113155
return {'stdout': run_result}
114156

115157
def mocked_has_package(model, package_name):
116158
assert model is InstalledRedHatSignedRPM
117159
assert package_name == run_result
118-
return package_exists
160+
return is_installed_rh_signed_package
161+
162+
def mocked_read_file(path):
163+
assert path == config_path
164+
return config_contents
119165

120166
monkeypatch.setattr(checkldsoconfiguration, 'run', mocked_run)
121167
monkeypatch.setattr(checkldsoconfiguration, 'has_package', mocked_has_package)
168+
monkeypatch.setattr(checkldsoconfiguration, '_read_file', mocked_read_file)
169+
monkeypatch.setattr(checkldsoconfiguration, '_is_modified', lambda *_: is_modified)
122170
monkeypatch.setattr(os.path, 'isfile', lambda _: True)
123171

124172
result = checkldsoconfiguration._is_included_ld_so_config_custom(config_path)
173+
is_custom = not isinstance(run_result, str) or not is_installed_rh_signed_package or is_modified
174+
is_custom &= has_effective_lines
125175
assert result == is_custom

0 commit comments

Comments
 (0)