Merge pull request #27534 from keszybz/deperlify

Rewrite udev-test.pl in Python
This commit is contained in:
Yu Watanabe 2023-05-09 18:06:51 +09:00 committed by GitHub
commit c093bfe0ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 2556 additions and 3008 deletions

View file

@ -48,7 +48,6 @@ PACKAGES=(
mold
mount
net-tools
perl
python3-evdev
python3-jinja2
python3-lxml

View file

@ -19,7 +19,6 @@ ADDITIONAL_DEPS=(
libtss2-dev
libxkbcommon-dev
libzstd-dev
perl
python3-libevdev
python3-pefile
python3-pyelftools

View file

@ -45,7 +45,6 @@ The following exceptions apply:
* the following sources are licensed under the **CC0-1.0** license:
- src/basic/siphash24.c
- src/basic/siphash24.h
- tools/check-includes.pl
* the following sources are licensed under the **MIT-0** license:
- all examples under man/
- src/systemctl/systemd-sysv-install.SKELETON

12
README
View file

@ -195,7 +195,6 @@ REQUIREMENTS:
libgcrypt (optional)
libqrencode (optional)
libmicrohttpd (optional)
libpython (optional)
libidn2 or libidn (optional)
gnutls >= 3.1.4 (optional, >= 3.6.0 is required to support DNS-over-TLS with gnutls)
openssl >= 1.1.0 (optional, required to support DNS-over-TLS with openssl)
@ -206,17 +205,17 @@ REQUIREMENTS:
gperf
docbook-xsl (optional, required for documentation)
xsltproc (optional, required for documentation)
python >= 3.7 (required by meson too, >= 3.9 is required for ukify)
python-jinja2
python-pefile
python-pefile (optional, required for ukify)
python-lxml (optional, required to build the indices)
python >= 3.5
pyelftools (optional, required for systemd-boot)
meson >= 0.53.2
ninja
gcc >= 4.7
awk, sed, grep, and similar tools
clang >= 10.0, llvm >= 10.0 (optional, required to build BPF programs
from source code in C)
pyelftools (optional, required for systemd-boot)
During runtime, you need the following additional
dependencies:
@ -270,8 +269,9 @@ REQUIREMENTS:
Additional packages are necessary to run some tests:
- busybox (used by test/TEST-13-NSPAWN-SMOKE)
- nc (used by test/TEST-12-ISSUE-3171)
- python3-pyparsing
- python3-evdev (used by hwdb parsing tests)
- python (test-udev which is installed is in python)
- python-pyparsing
- python-evdev (used by hwdb parsing tests)
- strace (used by test/test-functions)
- capsh (optional, used by test-execute)

View file

@ -701,7 +701,6 @@ stat = find_program('stat')
ln = find_program('ln')
git = find_program('git', required : false)
env = find_program('env')
perl = find_program('perl', required : false)
rsync = find_program('rsync', required : false)
diff = find_program('diff')
find = find_program('find')
@ -2132,7 +2131,7 @@ endif
conf.set10('ENABLE_UKIFY', want_ukify)
############################################################
#
elf2efi_lds = project_source_root / 'tools/elf2efi.lds'
elf2efi_py = find_program('tools/elf2efi.py')
export_dbus_interfaces_py = find_program('tools/dbus_exporter.py')
@ -4416,6 +4415,7 @@ foreach test : simple_tests
tests += { 'sources' : [test] }
endforeach
TESTS = {}
foreach test : tests
sources = test.get('sources')
condition = test.get('condition', '')
@ -4462,11 +4462,13 @@ foreach test : tests
message('@0@ is an unsafe test'.format(name))
elif want_tests != 'false'
test(name, exe,
env : test_env,
timeout : test.get('timeout', 30),
suite : suite,
is_parallel : test.get('parallel', true))
env : test_env,
timeout : test.get('timeout', 30),
suite : suite,
is_parallel : test.get('parallel', true))
endif
TESTS += { name : exe }
endforeach
exe = executable(
@ -4528,6 +4530,16 @@ if want_tests != 'false' and static_libudev_pic
test('test-libudev-static-sym', exe)
endif
if want_tests != 'false'
udev_rule_runner = TESTS['udev-rule-runner'].full_path()
test('test-udev',
test_udev_py,
args : ['-v'],
env : ['UDEV_RULE_RUNNER=@0@'.format(udev_rule_runner)],
timeout : 180)
endif
############################################################
foreach fuzzer : simple_fuzzers
@ -4726,7 +4738,7 @@ if git.found()
all_files = run_command(
env, '-u', 'GIT_WORK_TREE',
git, '--git-dir=@0@/.git'.format(project_source_root),
'ls-files', ':/*.[ch]',
'ls-files', ':/*.[ch]', ':/*.cc',
check : false)
if all_files.returncode() == 0
all_files = files(all_files.stdout().split())
@ -4738,17 +4750,26 @@ if git.found()
run_target(
'ctags',
command : [env, 'ctags', '--tag-relative=never', '-o', '@0@/tags'.format(project_source_root)] + all_files)
endif
endif
if git.found()
############################################
if want_tests != 'false' and conf.get('BUILD_MODE_DEVELOPER') == 1
test('check-includes',
files('tools/check-includes.py'),
args: all_files,
env : ['PROJECT_SOURCE_ROOT=@0@'.format(project_source_root)])
endif
endif
####################################################
git_contrib_sh = find_program('tools/git-contrib.sh')
run_target(
'git-contrib',
command : [git_contrib_sh])
endif
if git.found()
####################################################
git_head = run_command(
git, '--git-dir=@0@/.git'.format(project_source_root),
'rev-parse', 'HEAD',

View file

@ -23,7 +23,6 @@ BuildPackages=
glib2
libxslt
linux-api-headers
perl
python
python-jinja
python-lxml

View file

@ -29,7 +29,6 @@ BuildPackages=
findutils
libxslt
pam-devel
perl-interpreter
pkgconfig(audit)
pkgconfig(blkid)
pkgconfig(bzip2)

View file

@ -99,8 +99,6 @@ enum JobResult {
_JOB_RESULT_INVALID = -EINVAL,
};
#include "unit.h"
struct JobDependency {
/* Encodes that the 'subject' job needs the 'object' job in
* some way. This structure is used only while building a transaction. */

View file

@ -409,7 +409,7 @@ tests += [
'timeout' : 120,
},
{
'sources' : files('test-udev.c'),
'sources' : files('udev-rule-runner.c'),
'link_with' : [
libshared,
libudevd_core,

View file

@ -18,6 +18,7 @@
#include "mkdir-label.h"
#include "mount-util.h"
#include "namespace-util.h"
#include "parse-util.h"
#include "selinux-util.h"
#include "signal-util.h"
#include "string-util.h"
@ -61,11 +62,11 @@ static int fake_filesystems(void) {
const char *error;
bool ignore_mount_error;
} fakefss[] = {
{ "test/tmpfs/sys", "/sys", "Failed to mount test /sys", false },
{ "test/tmpfs/dev", "/dev", "Failed to mount test /dev", false },
{ "test/run", "/run", "Failed to mount test /run", false },
{ "test/run", "/etc/udev/rules.d", "Failed to mount empty /etc/udev/rules.d", true },
{ "test/run", UDEVLIBEXECDIR "/rules.d", "Failed to mount empty " UDEVLIBEXECDIR "/rules.d", true },
{ "tmpfs/sys", "/sys", "Failed to mount test /sys", false },
{ "tmpfs/dev", "/dev", "Failed to mount test /dev", false },
{ "run", "/run", "Failed to mount test /run", false },
{ "run", "/etc/udev/rules.d", "Failed to mount empty /etc/udev/rules.d", true },
{ "run", UDEVLIBEXECDIR "/rules.d", "Failed to mount empty " UDEVLIBEXECDIR "/rules.d", true },
};
int r;
@ -92,9 +93,9 @@ static int run(int argc, char *argv[]) {
test_setup_logging(LOG_INFO);
if (!IN_SET(argc, 2, 3))
if (!IN_SET(argc, 2, 3, 4))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"This program needs one or two arguments, %d given", argc - 1);
"This program needs between one and three arguments, %d given", argc - 1);
r = fake_filesystems();
if (r < 0)
@ -123,10 +124,18 @@ static int run(int argc, char *argv[]) {
action = argv[1];
devpath = argv[2];
if (argv[3]) {
unsigned us;
r = safe_atou(argv[3], &us);
if (r < 0)
return log_error_errno(r, "Invalid delay '%s': %m", argv[3]);
usleep(us);
}
assert_se(udev_rules_load(&rules, RESOLVE_NAME_EARLY) == 0);
const char *syspath;
syspath = strjoina("/sys", devpath);
const char *syspath = strjoina("/sys", devpath);
r = device_new_from_synthetic_event(&dev, syspath, action);
if (r < 0)
return log_debug_errno(r, "Failed to open device '%s'", devpath);

View file

@ -18,13 +18,15 @@ import textwrap
try:
import pytest
except ImportError:
except ImportError as e:
print(str(e), file=sys.stderr)
sys.exit(77)
try:
# pyflakes: noqa
import pefile # noqa
except ImportError:
except ImportError as e:
print(str(e), file=sys.stderr)
sys.exit(77)
# We import ukify.py, which is a template file. But only __version__ is

View file

@ -123,23 +123,14 @@ endif
############################################################
# prepare test/sys tree
sys_script_py = find_program('sys-script.py')
custom_target(
'sys',
command : [sys_script_py, meson.current_build_dir()],
output : 'sys',
build_by_default : want_tests != 'false')
sys_script_py = files('sys-script.py')
test_udev_py = files('test-udev.py')
if perl.found()
udev_test_pl = find_program('udev-test.pl')
if want_tests != 'false'
test('udev-test',
udev_test_pl,
timeout : 180)
endif
else
message('Skipping udev-test because perl is not available')
if install_tests
install_data(
sys_script_py,
test_udev_py,
install_dir : unittestsdir)
endif
############################################################

View file

@ -52,6 +52,9 @@ for test in tests:
elif ex.returncode == 77:
print(f'{YELLOW}SKIP: {name}{RESET_ALL}')
total.skip += 1
elif ex.returncode == 127:
print(f'{YELLOW}SKIP: {name} (no interpeter) {RESET_ALL}')
total.skip += 1
else:
print(f'{RED}FAIL: {name}{RESET_ALL}')
total.fail += 1

View file

@ -4,4 +4,4 @@ Description=https://github.com/systemd/systemd/issues/2637
[Service]
Type=oneshot
ExecStart=/bin/sh -x -c "! test -x perl || perl -e 'exit(!(qq{%%U} eq qq{\\x25U}))'"
ExecStart=/bin/bash -x -c "[[ %%U == ?U ]]"

View file

@ -818,19 +818,19 @@ install_valgrind() {
exit 1
fi
local valgrind_bins valgrind_libs valgrind_dbg_and_supp
local valgrind_bins valgrind_libs valgrind_supp
readarray -t valgrind_bins < <(strace -e execve valgrind /bin/true 2>&1 >/dev/null | perl -lne 'print $1 if /^execve\("([^"]+)"/')
readarray -t valgrind_bins < <(strace -e execve valgrind /bin/true 2>&1 >/dev/null |
sed -r -n 's/execve\("([^"]*)".*/\1/p')
image_install "${valgrind_bins[@]}"
readarray -t valgrind_libs < <(LD_DEBUG=files valgrind /bin/true 2>&1 >/dev/null | perl -lne 'print $1 if m{calling init: (/.*vgpreload_.*)}')
readarray -t valgrind_libs < <(LD_DEBUG=files valgrind /bin/true 2>&1 >/dev/null |
sed -r -n 's|.*calling init: (/.*vgpreload_.*)|\1|p')
image_install "${valgrind_libs[@]}"
readarray -t valgrind_dbg_and_supp < <(
strace -e open valgrind /bin/true 2>&1 >/dev/null |
perl -lne 'if (my ($fname) = /^open\("([^"]+).*= (?!-)\d+/) { print $fname if $fname =~ /debug|\.supp$/ }'
)
image_install "${valgrind_dbg_and_supp[@]}"
readarray -t valgrind_supp < <(strace -e open valgrind /bin/true 2>&1 >/dev/null |
sed -r -n 's,open\("([^"]*(/debug[^"]*|\.supp))".*= [0-9].*,\1,p')
image_install "${valgrind_supp[@]}"
}
create_valgrind_wrapper() {
@ -1455,16 +1455,13 @@ check_asan_reports() {
ret=$((ret+1))
fi
# May 08 13:23:31 H testleak[2907148]: SUMMARY: AddressSanitizer: 4 byte(s) leaked in 1 allocation(s).
pids="$(
"$JOURNALCTL" -D "$root/var/log/journal" | perl -alne '
BEGIN {
%services_to_ignore = (
"dbus-daemon" => undef,
"dbus-broker-launch" => undef,
);
}
print $2 if /\s(\S*)\[(\d+)\]:\s*SUMMARY:\s+\w+Sanitizer/ && !exists $services_to_ignore{$1}'
"$JOURNALCTL" -D "$root/var/log/journal" --grep 'SUMMARY: .*Sanitizer:' |
grep -v -E 'dbus-daemon|dbus-broker-launch' |
sed -r -n 's/.* .+\[([0-9]+)\]: SUMMARY:.*/\1/p'
)"
if [[ -n "$pids" ]]; then
ret=$((ret+1))
for pid in $pids; do

2434
test/test-udev.py Executable file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -24,7 +24,7 @@ function report_result() {
local name="${1##*/}"
local ret=$2
if [[ $ret -ne 0 && $ret != 77 ]]; then
if [[ $ret -ne 0 && $ret != 77 && $ret != 127 ]]; then
echo "$name failed with $ret"
echo "$name" >>/failed-tests
{
@ -32,7 +32,7 @@ function report_result() {
cat "/$name.log"
echo "--- $name end ---"
} >>/failed
elif [[ $ret == 77 ]]; then
elif [[ $ret == 77 || $ret == 127 ]]; then
echo "$name skipped"
echo "$name" >>/skipped-tests
{

View file

@ -1,23 +0,0 @@
# SPDX-License-Identifier: CC0-1.0
#!/usr/bin/env perl
#
# checkincludes: Find files included more than once in (other) files.
foreach $file (@ARGV) {
open(FILE, $file) or die "Cannot open $file: $!.\n";
my %includedfiles = ();
while (<FILE>) {
if (m/^\s*#\s*include\s*[<"](\S*)[>"]/o) {
++$includedfiles{$1};
}
}
foreach $filename (keys %includedfiles) {
if ($includedfiles{$filename} > 1) {
print "$file: $filename is included more than once.\n";
}
}
close(FILE);
}

32
tools/check-includes.py Executable file
View file

@ -0,0 +1,32 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-2.1-or-later
# pylint: disable=missing-docstring,invalid-name,unspecified-encoding,consider-using-with
import os
import pathlib
import re
import sys
PROJECT_ROOT = pathlib.Path(os.getenv('PROJECT_SOURCE_ROOT', '.'))
def check_file(filename):
seen = set()
good = True
for n, line in enumerate(open(filename)):
m = re.match(r'^\s*#\s*include\s*[<"](\S*)[>"]', line)
if m:
include = m.group(1)
if include in seen:
try:
filename = pathlib.Path(filename).resolve().relative_to(PROJECT_ROOT)
except ValueError:
pass
print(f'{filename}:{n}: {line.strip()}')
good = False
seen.add(include)
return good
if __name__ == '__main__':
good = all(check_file(name) for name in sys.argv[1:])
sys.exit(0 if good else 1)

View file

@ -109,12 +109,12 @@ install -Dt "$OUT/src/shared/" \
# Most i386 libraries have to be brought to the runtime environment somehow. Ideally they
# should be linked statically but since it isn't possible another way to keep them close
# to the fuzz targets is used here. The dependencies are copied to "$OUT/src/shared" and
# then `rpath` is tweaked to make it possible for the linker to find them there. "$OUT/src/shared"
# then 'rpath' is tweaked to make it possible for the linker to find them there. "$OUT/src/shared"
# is chosen because the runtime search path of all the fuzz targets already points to it
# to load "libsystemd-shared" and "libsystemd-core". Stuff like that should be avoided on
# x86_64 because it tends to break coverage reports, fuzz-introspector, CIFuzz and so on.
if [[ "$ARCHITECTURE" == i386 ]]; then
for lib_path in $(ldd "$OUT"/src/shared/libsystemd-shared-*.so | perl -lne 'print $1 if m{=>\s+(/lib\S+)}'); do
for lib_path in $(ldd "$OUT"/src/shared/libsystemd-shared-*.so | awk '/=> \/lib/ { print $3 }'); do
lib_name=$(basename "$lib_path")
cp "$lib_path" "$OUT/src/shared"
patchelf --set-rpath \$ORIGIN "$OUT/src/shared/$lib_name"