Compare commits

...

7 Commits

Author SHA1 Message Date
Zbigniew Jędrzejewski-Szmek 580acef5c7
Merge 0e54562082 into 4a346b779a 2024-11-25 13:50:41 +01:00
Daan De Meyer 4a346b779a test: Dump coredumps from journal in the integration test wrapper
Fixes #35277
2024-11-25 19:12:11 +09:00
Yu Watanabe 0e42004f3e networkd-test.py: disable IPv6AcceptRA= if not necessary
To speed up the test. Otherwise, it takes about few seconds interfaces
to enter the configured state. And may networkd-wait-online timeouts.
2024-11-25 10:07:26 +00:00
Luca Boccassi 6fd3496cfd test: mask tmpfiles.d file shipped by selinux policy package in containers
This tmpfiles.d wants to write to sysfs, which is read-only in containers,
so systemd-tmpfiles --create fails in TEST-22-TMPFILES when ran in nspawn
if the selinux policy package is instealled. Mask it, as it's not our
config file, we don't need it in the test.
2024-11-25 15:25:55 +09:00
Daan De Meyer bb486fe9df mkosi: Use shared extra tree between initrd and main image
Let's share more between initrd and main system and use a shared
extra tree to achieve that.
2024-11-25 15:09:58 +09:00
Zbigniew Jędrzejewski-Szmek 0e54562082 logind: define flags enum for manager_is_inhibited()
The most common case of block=true, ignore_inactive=false is mapped to flags=0.

For https://github.com/systemd/systemd/issues/34091.
2024-11-20 13:23:12 +01:00
Zbigniew Jędrzejewski-Szmek 03af199aaf logind: drop one duplicate param in manager_is_inhibited()
In the review in https://github.com/systemd/systemd/pull/30307#pullrequestreview-2255002732
removal of the excessive boolean parameters was requested. We don't need
a separate boolean param here, since we always pass true with a uid and
false otherwise.
2024-11-20 09:44:01 +01:00
23 changed files with 156 additions and 60 deletions

View File

@ -38,9 +38,8 @@ SignExpectedPcr=yes
[Content] [Content]
ExtraTrees= ExtraTrees=
mkosi.extra.common
mkosi.crt:/usr/lib/verity.d/mkosi.crt # sysext verification key mkosi.crt:/usr/lib/verity.d/mkosi.crt # sysext verification key
mkosi.leak-sanitizer-suppressions:/usr/lib/systemd/leak-sanitizer-suppressions
mkosi.coredump-journal-storage.conf:/usr/lib/systemd/coredump.conf.d/10-coredump-journal-storage.conf
%O/minimal-0.root-%a.raw:/usr/share/minimal_0.raw %O/minimal-0.root-%a.raw:/usr/share/minimal_0.raw
%O/minimal-0.root-%a-verity.raw:/usr/share/minimal_0.verity %O/minimal-0.root-%a-verity.raw:/usr/share/minimal_0.verity
%O/minimal-0.root-%a-verity-sig.raw:/usr/share/minimal_0.verity.sig %O/minimal-0.root-%a-verity-sig.raw:/usr/share/minimal_0.verity.sig

View File

@ -6,9 +6,7 @@ Include=
%D/mkosi.sanitizers %D/mkosi.sanitizers
[Content] [Content]
ExtraTrees= ExtraTrees=%D/mkosi.extra.common
%D/mkosi.leak-sanitizer-suppressions:/usr/lib/systemd/leak-sanitizer-suppressions
%D/mkosi.coredump-journal-storage.conf:/usr/lib/systemd/coredump.conf.d/10-coredump-journal-storage.conf
Packages= Packages=
findutils findutils

View File

@ -234,7 +234,7 @@ static int handle_action_execute(
/* If the actual operation is inhibited, warn and fail */ /* If the actual operation is inhibited, warn and fail */
if (inhibit_what_is_valid(inhibit_operation) && if (inhibit_what_is_valid(inhibit_operation) &&
!ignore_inhibited && !ignore_inhibited &&
manager_is_inhibited(m, inhibit_operation, /* block= */ true, NULL, false, false, 0, &offending)) { manager_is_inhibited(m, inhibit_operation, NULL, /* flags= */ 0, UID_INVALID, &offending)) {
_cleanup_free_ char *comm = NULL, *u = NULL; _cleanup_free_ char *comm = NULL, *u = NULL;
(void) pidref_get_comm(&offending->pid, &comm); (void) pidref_get_comm(&offending->pid, &comm);
@ -372,7 +372,7 @@ int manager_handle_action(
/* If the key handling is inhibited, don't do anything */ /* If the key handling is inhibited, don't do anything */
if (inhibit_key > 0) { if (inhibit_key > 0) {
if (manager_is_inhibited(m, inhibit_key, /* block= */ true, NULL, true, false, 0, NULL)) { if (manager_is_inhibited(m, inhibit_key, NULL, MANAGER_IS_INHIBITED_IGNORE_INACTIVE, UID_INVALID, NULL)) {
log_debug("Refusing %s operation, %s is inhibited.", log_debug("Refusing %s operation, %s is inhibited.",
handle_action_to_string(handle), handle_action_to_string(handle),
inhibit_what_to_string(inhibit_key)); inhibit_what_to_string(inhibit_key));

View File

@ -411,7 +411,7 @@ int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
assert(m); assert(m);
idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, /* block= */ true, t, false, false, 0, NULL); idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, t, /* flags= */ 0, UID_INVALID, NULL);
HASHMAP_FOREACH(s, m->sessions) { HASHMAP_FOREACH(s, m->sessions) {
dual_timestamp k; dual_timestamp k;

View File

@ -1931,7 +1931,12 @@ int manager_dispatch_delayed(Manager *manager, bool timeout) {
if (!manager->delayed_action || manager->action_job) if (!manager->delayed_action || manager->action_job)
return 0; return 0;
if (manager_is_inhibited(manager, manager->delayed_action->inhibit_what, /* block= */ false, NULL, false, false, 0, &offending)) { if (manager_is_inhibited(manager,
manager->delayed_action->inhibit_what,
NULL,
MANAGER_IS_INHIBITED_CHECK_DELAY,
UID_INVALID,
&offending)) {
_cleanup_free_ char *comm = NULL, *u = NULL; _cleanup_free_ char *comm = NULL, *u = NULL;
if (!timeout) if (!timeout)
@ -2033,7 +2038,7 @@ int bus_manager_shutdown_or_sleep_now_or_later(
delayed = delayed =
m->inhibit_delay_max > 0 && m->inhibit_delay_max > 0 &&
manager_is_inhibited(m, a->inhibit_what, /* block= */ false, NULL, false, false, 0, NULL); manager_is_inhibited(m, a->inhibit_what, NULL, MANAGER_IS_INHIBITED_CHECK_DELAY, UID_INVALID, NULL);
if (delayed) if (delayed)
/* Shutdown is delayed, keep in mind what we /* Shutdown is delayed, keep in mind what we
@ -2077,7 +2082,7 @@ static int verify_shutdown_creds(
return r; return r;
multiple_sessions = r > 0; multiple_sessions = r > 0;
blocked = manager_is_inhibited(m, a->inhibit_what, /* block= */ true, NULL, false, true, uid, &offending); blocked = manager_is_inhibited(m, a->inhibit_what, NULL, /* flags= */ 0, uid, &offending);
interactive = flags & SD_LOGIND_INTERACTIVE; interactive = flags & SD_LOGIND_INTERACTIVE;
if (multiple_sessions) { if (multiple_sessions) {
@ -2820,7 +2825,7 @@ static int method_can_shutdown_or_sleep(
return r; return r;
multiple_sessions = r > 0; multiple_sessions = r > 0;
blocked = manager_is_inhibited(m, a->inhibit_what, /* block= */ true, NULL, false, true, uid, NULL); blocked = manager_is_inhibited(m, a->inhibit_what, NULL, /* flags= */ 0, uid, NULL);
if (check_unit_state && a->target) { if (check_unit_state && a->target) {
_cleanup_free_ char *load_state = NULL; _cleanup_free_ char *load_state = NULL;

View File

@ -399,11 +399,9 @@ static int pidref_is_active_session(Manager *m, const PidRef *pid) {
bool manager_is_inhibited( bool manager_is_inhibited(
Manager *m, Manager *m,
InhibitWhat w, InhibitWhat w,
bool block,
dual_timestamp *since, dual_timestamp *since,
bool ignore_inactive, ManagerIsInhibitedFlags flags,
bool ignore_uid, uid_t uid_to_ignore,
uid_t uid,
Inhibitor **ret_offending) { Inhibitor **ret_offending) {
Inhibitor *i, *offending = NULL; Inhibitor *i, *offending = NULL;
@ -421,18 +419,19 @@ bool manager_is_inhibited(
if (!(i->what & w)) if (!(i->what & w))
continue; continue;
if ((block && !IN_SET(i->mode, INHIBIT_BLOCK, INHIBIT_BLOCK_WEAK)) || if ((flags & MANAGER_IS_INHIBITED_CHECK_DELAY) != (i->mode == INHIBIT_DELAY))
(!block && i->mode != INHIBIT_DELAY))
continue; continue;
if (ignore_inactive && pidref_is_active_session(m, &i->pid) <= 0) if ((flags & MANAGER_IS_INHIBITED_IGNORE_INACTIVE) &&
pidref_is_active_session(m, &i->pid) <= 0)
continue; continue;
if (i->mode == INHIBIT_BLOCK_WEAK && ignore_uid && i->uid == uid) if (i->mode == INHIBIT_BLOCK_WEAK &&
uid_is_valid(uid_to_ignore) &&
uid_to_ignore == i->uid)
continue; continue;
if (!inhibited || if (!inhibited || i->since.monotonic < ts.monotonic)
i->since.monotonic < ts.monotonic)
ts = i->since; ts = i->since;
inhibited = true; inhibited = true;

View File

@ -67,7 +67,20 @@ int inhibitor_create_fifo(Inhibitor *i);
bool inhibitor_is_orphan(Inhibitor *i); bool inhibitor_is_orphan(Inhibitor *i);
InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mode); InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mode);
bool manager_is_inhibited(Manager *m, InhibitWhat w, bool block, dual_timestamp *since, bool ignore_inactive, bool ignore_uid, uid_t uid, Inhibitor **offending);
typedef enum ManagerIsInhibitedFlags {
MANAGER_IS_INHIBITED_CHECK_DELAY = 1 << 0, /* When set, we only check delay inhibitors.
* Otherwise, we only check block inhibitors. */
MANAGER_IS_INHIBITED_IGNORE_INACTIVE = 1 << 1, /* When set, ignore inactive sessions. */
} ManagerIsInhibitedFlags;
bool manager_is_inhibited(
Manager *m,
InhibitWhat w,
dual_timestamp *since,
ManagerIsInhibitedFlags flags,
uid_t uid_to_ignore,
Inhibitor **ret_offending);
static inline bool inhibit_what_is_valid(InhibitWhat w) { static inline bool inhibit_what_is_valid(InhibitWhat w) {
return w > 0 && w < _INHIBIT_WHAT_MAX; return w > 0 && w < _INHIBIT_WHAT_MAX;

View File

@ -3,6 +3,7 @@
integration_tests += [ integration_tests += [
integration_test_template + { integration_test_template + {
'name' : fs.name(meson.current_source_dir()), 'name' : fs.name(meson.current_source_dir()),
'coredump-exclude-regex' : '/(bash|python3.[0-9]+|systemd-executor)$',
'cmdline' : integration_test_template['cmdline'] + [ 'cmdline' : integration_test_template['cmdline'] + [
''' '''

View File

@ -4,6 +4,7 @@ integration_tests += [
integration_test_template + { integration_test_template + {
'name' : fs.name(meson.current_source_dir()), 'name' : fs.name(meson.current_source_dir()),
'unit' : files('TEST-16-EXTEND-TIMEOUT.service'), 'unit' : files('TEST-16-EXTEND-TIMEOUT.service'),
'coredump-exclude-regex' : '/(bash|sleep)$',
}, },
] ]

View File

@ -4,5 +4,6 @@ integration_tests += [
integration_test_template + { integration_test_template + {
'name' : fs.name(meson.current_source_dir()), 'name' : fs.name(meson.current_source_dir()),
'vm' : true, 'vm' : true,
'coredump-exclude-regex' : '/(sleep|udevadm)$',
}, },
] ]

View File

@ -3,5 +3,6 @@
integration_tests += [ integration_tests += [
integration_test_template + { integration_test_template + {
'name' : fs.name(meson.current_source_dir()), 'name' : fs.name(meson.current_source_dir()),
'coredump-exclude-regex' : '/(sleep|bash|systemd-notify)$',
}, },
] ]

View File

@ -4,5 +4,7 @@ integration_tests += [
integration_test_template + { integration_test_template + {
'name' : fs.name(meson.current_source_dir()), 'name' : fs.name(meson.current_source_dir()),
'priority' : 10, 'priority' : 10,
# TODO: Remove when https://github.com/systemd/systemd/issues/35335 is fixed.
'coredump-exclude-regex' : '/systemd-localed',
}, },
] ]

View File

@ -5,6 +5,7 @@ integration_tests += [
'name' : fs.name(meson.current_source_dir()), 'name' : fs.name(meson.current_source_dir()),
'storage': 'persistent', 'storage': 'persistent',
'vm' : true, 'vm' : true,
'coredump-exclude-regex' : '/(test-usr-dump|test-dump|bash)$',
}, },
] ]

View File

@ -6,6 +6,7 @@
import argparse import argparse
import json import json
import os import os
import re
import shlex import shlex
import subprocess import subprocess
import sys import sys
@ -32,6 +33,59 @@ ExecStart=false
""" """
def process_coredumps(args: argparse.Namespace, journal_file: Path) -> bool:
# Collect executable paths of all coredumps and filter out the expected ones.
if args.coredump_exclude_regex:
exclude_regex = re.compile(args.coredump_exclude_regex)
else:
exclude_regex = None
result = subprocess.run(
[
args.mkosi,
'--directory', os.fspath(args.meson_source_dir),
'--extra-search-path', os.fspath(args.meson_build_dir),
'sandbox',
'coredumpctl',
'--file', journal_file,
'--json=short',
],
stdout=subprocess.PIPE,
text=True,
) # fmt: skip
# coredumpctl returns a non-zero exit status if there are no coredumps.
if result.returncode != 0:
return False
coredumps = json.loads(result.stdout)
coredumps = [
coredump for coredump in coredumps if not exclude_regex or not exclude_regex.search(coredump['exe'])
]
if not coredumps:
return False
subprocess.run(
[
args.mkosi,
'--directory', os.fspath(args.meson_source_dir),
'--extra-search-path', os.fspath(args.meson_build_dir),
'sandbox',
'coredumpctl',
'--file', journal_file,
'--no-pager',
'info',
*(coredump['exe'] for coredump in coredumps),
],
check=True,
) # fmt: skip
return True
def main() -> None: def main() -> None:
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--mkosi', required=True) parser.add_argument('--mkosi', required=True)
@ -44,6 +98,7 @@ def main() -> None:
parser.add_argument('--slow', action=argparse.BooleanOptionalAction) parser.add_argument('--slow', action=argparse.BooleanOptionalAction)
parser.add_argument('--vm', action=argparse.BooleanOptionalAction) parser.add_argument('--vm', action=argparse.BooleanOptionalAction)
parser.add_argument('--exit-code', required=True, type=int) parser.add_argument('--exit-code', required=True, type=int)
parser.add_argument('--coredump-exclude-regex', required=True)
parser.add_argument('mkosi_args', nargs='*') parser.add_argument('mkosi_args', nargs='*')
args = parser.parse_args() args = parser.parse_args()
@ -114,7 +169,9 @@ def main() -> None:
""" """
) )
journal_file = None journal_file = (args.meson_build_dir / (f'test/journal/{name}.journal')).absolute()
journal_file.unlink(missing_ok=True)
if not sys.stderr.isatty(): if not sys.stderr.isatty():
dropin += textwrap.dedent( dropin += textwrap.dedent(
""" """
@ -122,9 +179,6 @@ def main() -> None:
FailureAction=exit FailureAction=exit
""" """
) )
journal_file = (args.meson_build_dir / (f'test/journal/{name}.journal')).absolute()
journal_file.unlink(missing_ok=True)
elif not shell: elif not shell:
dropin += textwrap.dedent( dropin += textwrap.dedent(
""" """
@ -194,15 +248,16 @@ def main() -> None:
) )
exit(77) exit(77)
if journal_file and ( coredumps = process_coredumps(args, journal_file)
keep_journal == '0' or (result.returncode in (args.exit_code, 77) and keep_journal == 'fail')
if keep_journal == '0' or (
keep_journal == 'fail' and result.returncode in (args.exit_code, 77) and not coredumps
): ):
journal_file.unlink(missing_ok=True) journal_file.unlink(missing_ok=True)
if shell or result.returncode in (args.exit_code, 77): if shell or (result.returncode in (args.exit_code, 77) and not coredumps):
exit(0 if shell or result.returncode == args.exit_code else 77) exit(0 if shell or result.returncode == args.exit_code else 77)
if journal_file:
ops = [] ops = []
if os.getenv('GITHUB_ACTIONS'): if os.getenv('GITHUB_ACTIONS'):
@ -228,10 +283,7 @@ def main() -> None:
ops += [f'journalctl --file {journal_file} --no-hostname -o short-monotonic -u {args.unit} -p info'] ops += [f'journalctl --file {journal_file} --no-hostname -o short-monotonic -u {args.unit} -p info']
print( print("Test failed, relevant logs can be viewed with: \n\n" f"{(' && '.join(ops))}\n", file=sys.stderr)
"Test failed, relevant logs can be viewed with: \n\n" f"{(' && '.join(ops))}\n",
file=sys.stderr,
)
# 0 also means we failed so translate that to a non-zero exit code to mark the test as failed. # 0 also means we failed so translate that to a non-zero exit code to mark the test as failed.
exit(result.returncode or 1) exit(result.returncode or 1)

View File

@ -297,6 +297,7 @@ integration_test_template = {
'qemu-args' : [], 'qemu-args' : [],
'exit-code' : 123, 'exit-code' : 123,
'vm' : false, 'vm' : false,
'coredump-exclude-regex' : '',
} }
testdata_subdirs = [ testdata_subdirs = [
'auxv', 'auxv',
@ -391,6 +392,7 @@ foreach integration_test : integration_tests
'--storage', integration_test['storage'], '--storage', integration_test['storage'],
'--firmware', integration_test['firmware'], '--firmware', integration_test['firmware'],
'--exit-code', integration_test['exit-code'].to_string(), '--exit-code', integration_test['exit-code'].to_string(),
'--coredump-exclude-regex', integration_test['coredump-exclude-regex'],
] ]
if 'unit' in integration_test if 'unit' in integration_test

View File

@ -248,6 +248,7 @@ Bridge=mybridge
[Match] [Match]
Name=mybridge Name=mybridge
[Network] [Network]
IPv6AcceptRA=no
DNS=192.168.250.1 DNS=192.168.250.1
Address=192.168.250.33/24 Address=192.168.250.33/24
Gateway=192.168.250.1 Gateway=192.168.250.1
@ -540,6 +541,7 @@ MACAddress=12:34:56:78:9a:bc
[Match] [Match]
Name=dummy0 Name=dummy0
[Network] [Network]
IPv6AcceptRA=no
Address=192.168.42.100/24 Address=192.168.42.100/24
DNS=192.168.42.1 DNS=192.168.42.1
Domains= ~company Domains= ~company
@ -573,6 +575,7 @@ MACAddress=12:34:56:78:9a:bc
self.write_network('50-myvpn.network', '''[Match] self.write_network('50-myvpn.network', '''[Match]
Name=dummy0 Name=dummy0
[Network] [Network]
IPv6AcceptRA=no
Address=192.168.42.100/24 Address=192.168.42.100/24
DNS=192.168.42.1 DNS=192.168.42.1
Domains= ~company ~. Domains= ~company ~.
@ -927,6 +930,7 @@ cat <<EOF >/run/systemd/network/50-test.network
Name={ifr} Name={ifr}
[Network] [Network]
IPv6AcceptRA=no
Address=192.168.5.1/24 Address=192.168.5.1/24
{addr6} {addr6}
DHCPServer=yes DHCPServer=yes
@ -1006,6 +1010,7 @@ MACAddress=12:34:56:78:9a:bc
[Match] [Match]
Name=dummy0 Name=dummy0
[Network] [Network]
IPv6AcceptRA=no
Address=192.168.42.100/24 Address=192.168.42.100/24
DNS=192.168.42.1 DNS=192.168.42.1
Domains= one two three four five six seven eight nine ten Domains= one two three four five six seven eight nine ten
@ -1035,6 +1040,7 @@ MACAddress=12:34:56:78:9a:bc
[Match] [Match]
Name=dummy0 Name=dummy0
[Network] [Network]
IPv6AcceptRA=no
Address=192.168.42.100/24 Address=192.168.42.100/24
DNS=192.168.42.1 DNS=192.168.42.1
''') ''')
@ -1107,7 +1113,12 @@ class MatchClientTest(unittest.TestCase, NetworkdTestingUtilities):
def test_basic_matching(self): def test_basic_matching(self):
"""Verify the Name= line works throughout this class.""" """Verify the Name= line works throughout this class."""
self.add_veth_pair('test_if1', 'fake_if2') self.add_veth_pair('test_if1', 'fake_if2')
self.write_network('50-test.network', "[Match]\nName=test_*\n[Network]") self.write_network('50-test.network', '''\
[Match]
Name=test_*
[Network]
IPv6AcceptRA=no
''')
subprocess.check_call(['systemctl', 'start', 'systemd-networkd']) subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
self.assert_link_states(test_if1='managed', fake_if2='unmanaged') self.assert_link_states(test_if1='managed', fake_if2='unmanaged')
@ -1118,11 +1129,13 @@ class MatchClientTest(unittest.TestCase, NetworkdTestingUtilities):
mac = '00:01:02:03:98:99' mac = '00:01:02:03:98:99'
self.add_veth_pair('test_veth', 'test_peer', self.add_veth_pair('test_veth', 'test_peer',
['addr', mac], ['addr', mac]) ['addr', mac], ['addr', mac])
self.write_network('50-no-veth.network', """\ self.write_network('50-no-veth.network', '''\
[Match] [Match]
MACAddress={} MACAddress={}
Name=!nonexistent *peer* Name=!nonexistent *peer*
[Network]""".format(mac)) [Network]
IPv6AcceptRA=no
'''.format(mac))
subprocess.check_call(['systemctl', 'start', 'systemd-networkd']) subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
self.assert_link_states(test_veth='managed', test_peer='unmanaged') self.assert_link_states(test_veth='managed', test_peer='unmanaged')

View File

@ -6,6 +6,14 @@ set -o pipefail
# shellcheck source=test/units/test-control.sh # shellcheck source=test/units/test-control.sh
. "$(dirname "$0")"/test-control.sh . "$(dirname "$0")"/test-control.sh
if systemd-detect-virt --quiet --container; then
# This comes from the selinux package and tries to write
# some files under sysfs, which will be read-only in a container,
# so mask it. It's not our tmpfiles.d file anyway.
mkdir -p /run/tmpfiles.d/
ln -s /dev/null /run/tmpfiles.d/selinux-policy.conf
fi
run_subtests run_subtests
touch /testok touch /testok