Merge pull request #26739 from ldv-alt/udevadm-verify

udevadm verify: introduce --root option
This commit is contained in:
Yu Watanabe
2023-03-11 17:12:57 +09:00
committed by GitHub
6 changed files with 197 additions and 73 deletions
+10 -3
View File
@@ -51,7 +51,7 @@
<cmdsynopsis>
<command>udevadm verify</command>
<arg choice="opt" rep="repeat">options</arg>
<arg choice="plain" rep="repeat"><replaceable>file</replaceable></arg>
<arg choice="opt" rep="repeat"><replaceable>file</replaceable></arg>
</cmdsynopsis>
<cmdsynopsis>
<command>udevadm wait <optional>options</optional> <replaceable>device|syspath</replaceable></command>
@@ -745,13 +745,15 @@
<refsect2>
<title>udevadm verify
<arg choice="opt"><replaceable>options</replaceable></arg>
<arg choice="plain" rep="repeat"><replaceable>file</replaceable></arg>
<arg choice="opt" rep="repeat"><replaceable>file</replaceable></arg>
…
</title>
<para>Verify syntactic and semantic correctness of udev rules files.</para>
<para>Positional arguments should be used to specify one or more files to check.</para>
<para>Positional arguments could be used to specify one or more files to check.
If no files are specified, the udev rules are read from the files located in
the same udev/rules.d directories that are processed by the udev daemon.</para>
<para>The exit status is <constant>0</constant> if all specified udev rules files
are syntactically and semantically correct, and a non-zero error code otherwise.</para>
@@ -767,6 +769,11 @@
parsed. When set to <constant>never</constant>, names will
never be resolved.</para>
</listitem>
<term><option>--root=<replaceable>PATH</replaceable></option></term>
<listitem>
<para>When looking for udev rules files located in udev/rules.d directories,
operate on files underneath the specified root path <replaceable>PATH</replaceable>.</para>
</listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
+5 -1
View File
@@ -64,7 +64,7 @@ _udevadm() {
[MONITOR_ARG]='-s --subsystem-match -t --tag-match'
[TEST]='-a --action -N --resolve-names'
[TEST_BUILTIN]='-a --action'
[VERIFY]='-N --resolve-names'
[VERIFY]='-N --resolve-names --root'
[WAIT]='-t --timeout --initialized=no --removed --settle'
[LOCK]='-t --timeout -d --device -b --backing -p --print'
)
@@ -254,6 +254,10 @@ _udevadm() {
-N|--resolve-names)
comps='early never'
;;
--root)
comps=$(compgen -A directory -- "$cur" )
compopt -o dirnames
;;
*)
comps=''
;;
+1
View File
@@ -108,6 +108,7 @@ _udevadm_test-builtin(){
_udevadm_verify(){
_arguments \
{-N+,--resolve-names=}'[When to resolve names.]:resolve:(early never)' \
'--root=[Operate on catalog hierarchy under specified directory]:directories:_directories' \
{-h,--help}'[Print help text.]' \
'*::files:_files'
}
+3
View File
@@ -1014,6 +1014,9 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
return log_token_invalid_attr(rules, key);
if (op != OP_ASSIGN)
return log_token_invalid_op(rules, key);
if (FLAGS_SET(rule_line->type, LINE_HAS_LABEL))
log_token_warning(rules, "Contains multiple LABEL keys, ignoring LABEL=\"%s\".",
rule_line->label);
rule_line->label = value;
SET_FLAG(rule_line->type, LINE_HAS_LABEL, true);
+40 -7
View File
@@ -7,13 +7,20 @@
#include <stdlib.h>
#include <unistd.h>
#include "conf-files.h"
#include "constants.h"
#include "log.h"
#include "parse-argument.h"
#include "pretty-print.h"
#include "static-destruct.h"
#include "strv.h"
#include "udev-rules.h"
#include "udevadm.h"
static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY;
static char *arg_root = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
@@ -23,11 +30,12 @@ static int help(void) {
if (r < 0)
return log_oom();
printf("%s verify [OPTIONS] FILE...\n"
printf("%s verify [OPTIONS] [FILE...]\n"
"\n%sVerify udev rules files.%s\n\n"
" -h --help Show this help\n"
" -V --version Show package version\n"
" -N --resolve-names=early|never When to resolve names\n"
" --root=PATH Operate on an alternate filesystem root\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
@@ -38,14 +46,18 @@ static int help(void) {
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_ROOT = 0x100,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "resolve-names", required_argument, NULL, 'N' },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "resolve-names", required_argument, NULL, 'N' },
{ "root", required_argument, NULL, ARG_ROOT },
{}
};
int c;
int r, c;
assert(argc >= 0);
assert(argv);
@@ -69,14 +81,21 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_resolve_name_timing == RESOLVE_NAME_NEVER)
arg_resolve_name_timing = RESOLVE_NAME_LATE;
break;
case ARG_ROOT:
r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root);
if (r < 0)
return r;
break;
case '?':
return -EINVAL;
default:
assert_not_reached();
}
if (optind == argc)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No rules file specified.");
if (arg_root && optind < argc)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Combination of --root= and FILEs is not supported.");
return 1;
}
@@ -121,5 +140,19 @@ int verify_main(int argc, char *argv[], void *userdata) {
if (!rules)
return -ENOMEM;
if (optind == argc) {
const char* const* rules_dirs = STRV_MAKE_CONST(CONF_PATHS("udev/rules.d"));
_cleanup_strv_free_ char **files = NULL;
r = conf_files_list_strv(&files, ".rules", arg_root, 0, rules_dirs);
if (r < 0)
return log_error_errno(r, "Failed to enumerate rules files: %m");
if (arg_root && strv_isempty(files))
return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
"No rules files found in %s", arg_root);
return verify_rules(rules, files);
}
return verify_rules(rules, strv_skip(argv, optind));
}
+138 -62
View File
@@ -5,70 +5,130 @@ set -o pipefail
# Test for udevadm verify.
udevadm verify -h
udevadm verify --help
udevadm verify -V
udevadm verify --version
udevadm verify /dev/null
# shellcheck source=test/units/assert.sh
. "$(dirname "$0")"/assert.sh
cleanup() {
cd /
rm -rf "${workdir}"
workdir=
}
workdir="$(mktemp -d)"
trap cleanup EXIT
cd "${workdir}"
test_number=0
rules=
exp=
err=
next_test_number() {
: $((++test_number))
local num_str
num_str=$(printf %05d "${test_number}")
rules="sample-${num_str}.rules"
exp="sample-${num_str}.exp"
err="sample-${num_str}.err"
}
assert_0() {
assert_rc 0 udevadm verify "$@"
next_test_number
}
assert_1() {
if [ -f "${exp}" ]; then
set +e
udevadm verify "$@" 2>"${err}"
assert_eq "$?" 1
set -e
diff "${exp}" "${err}"
else
set +e
udevadm verify "$@"
assert_eq "$?" 1
set -e
fi
next_test_number
}
assert_0 -h
assert_0 --help
assert_0 -V
assert_0 --version
assert_0 /dev/null
# No rules file specified.
(! udevadm verify)
# unrecognized option '--unknown'
(! udevadm verify --unknown)
assert_1 --unknown
# option requires an argument -- 'N'
(! udevadm verify -N)
assert_1 -N
# --resolve-names= takes "early" or "never"
(! udevadm verify -N now)
assert_1 -N now
# option '--resolve-names' requires an argument
(! udevadm verify --resolve-names)
assert_1 --resolve-names
# --resolve-names= takes "early" or "never"
(! udevadm verify --resolve-names=now)
assert_1 --resolve-names=now
# Failed to parse rules file .: Is a directory
(! udevadm verify .)
assert_1 .
# Failed to parse rules file .: Is a directory
(! udevadm verify /dev/null . /dev/null)
assert_1 /dev/null . /dev/null
rules_dir='etc/udev/rules.d'
mkdir -p "${rules_dir}"
# No rules files found in $PWD
assert_1 --root="${workdir}"
touch "${rules_dir}/empty.rules"
assert_0 --root="${workdir}"
# Combination of --root= and FILEs is not supported.
assert_1 --root="${workdir}" /dev/null
# No rules files found in nosuchdir
assert_1 --root=nosuchdir
cd "${rules_dir}"
# UDEV_LINE_SIZE 16384
printf '%16383s\n' ' ' >sample.rules
udevadm verify sample.rules
printf '%16383s\n' ' ' >"${rules}"
assert_0 "${rules}"
# Failed to parse rules file sample.rules: No buffer space available
printf '%16384s\n' ' ' >sample.rules
(! udevadm verify sample.rules)
# Failed to parse rules file ${rules}: No buffer space available
printf '%16384s\n' ' ' >"${rules}"
echo "Failed to parse rules file ${rules}: No buffer space available" >"${exp}"
assert_1 "${rules}"
printf 'RUN+="/bin/true"%8175s\\\n' ' ' ' ' >sample.rules
echo >>sample.rules
udevadm verify sample.rules
printf 'RUN+="/bin/true"%8175s\\\n' ' ' ' ' >"${rules}"
echo >>"${rules}"
assert_0 "${rules}"
printf 'RUN+="/bin/true"%8176s\\\n #\n' ' ' ' ' >sample.rules
echo >>sample.rules
cat >exp <<'EOF'
sample.rules:5 Line is too long, ignored
sample.rules: udev rules check failed
printf 'RUN+="/bin/true"%8176s\\\n #\n' ' ' ' ' >"${rules}"
echo >>"${rules}"
cat >"${exp}" <<EOF
${rules}:5 Line is too long, ignored
${rules}: udev rules check failed
EOF
(! udevadm verify sample.rules 2>err)
diff exp err
assert_1 "${rules}"
printf '\\\n' >sample.rules
cat >exp <<'EOF'
sample.rules:1 Unexpected EOF after line continuation, line ignored
sample.rules: udev rules check failed
printf '\\\n' >"${rules}"
cat >"${exp}" <<EOF
${rules}:1 Unexpected EOF after line continuation, line ignored
${rules}: udev rules check failed
EOF
(! udevadm verify sample.rules 2>err)
diff exp err
assert_1 "${rules}"
test_syntax_error() {
local rule msg
rule="$1"; shift
msg="$1"; shift
local rule msg
rule="$1"; shift
msg="$1"; shift
printf '%s\n' "$rule" > sample.rules
cat >exp <<-EOF
sample.rules:1 $msg
sample.rules: udev rules check failed
EOF
(! udevadm verify sample.rules 2>err)
diff exp err
printf '%s\n' "${rule}" >"${rules}"
cat >"${exp}" <<EOF
${rules}:1 ${msg}
${rules}: udev rules check failed
EOF
assert_1 "${rules}"
}
test_syntax_error '=' 'Invalid key/value pair, ignoring.'
@@ -147,12 +207,12 @@ test_syntax_error 'OWNER{a}="b"' 'Invalid attribute for OWNER.'
test_syntax_error 'OWNER-="b"' 'Invalid operator for OWNER.'
test_syntax_error 'OWNER!="b"' 'Invalid operator for OWNER.'
test_syntax_error 'OWNER+="0"' "OWNER key takes '=' or ':=' operator, assuming '='."
test_syntax_error 'OWNER="nosuchuser"' "Unknown user 'nosuchuser', ignoring"
test_syntax_error 'OWNER=":nosuchuser:"' "Unknown user ':nosuchuser:', ignoring"
test_syntax_error 'GROUP{a}="b"' 'Invalid attribute for GROUP.'
test_syntax_error 'GROUP-="b"' 'Invalid operator for GROUP.'
test_syntax_error 'GROUP!="b"' 'Invalid operator for GROUP.'
test_syntax_error 'GROUP+="0"' "GROUP key takes '=' or ':=' operator, assuming '='."
test_syntax_error 'GROUP="nosuchuser"' "Unknown group 'nosuchuser', ignoring"
test_syntax_error 'GROUP=":nosuchgroup:"' "Unknown group ':nosuchgroup:', ignoring"
test_syntax_error 'MODE{a}="b"' 'Invalid attribute for MODE.'
test_syntax_error 'MODE-="b"' 'Invalid operator for MODE.'
test_syntax_error 'MODE!="b"' 'Invalid operator for MODE.'
@@ -177,31 +237,47 @@ test_syntax_error 'LABEL=="b"' 'Invalid operator for LABEL.'
test_syntax_error 'LABEL="b"' 'LABEL="b" is unused.'
test_syntax_error 'a="b"' "Invalid key 'a'"
echo 'GOTO="a"' >sample.rules
cat >exp <<'EOF'
sample.rules:1 GOTO="a" has no matching label, ignoring
sample.rules:1 The line takes no effect any more, dropping
sample.rules: udev rules check failed
echo 'GOTO="a"' >"${rules}"
cat >"${exp}" <<EOF
${rules}:1 GOTO="a" has no matching label, ignoring
${rules}:1 The line takes no effect any more, dropping
${rules}: udev rules check failed
EOF
(! udevadm verify sample.rules 2>err)
diff exp err
assert_1 "${rules}"
cat >sample.rules <<'EOF'
cat >"${rules}" <<'EOF'
GOTO="a"
LABEL="a"
EOF
udevadm verify sample.rules
assert_0 "${rules}"
cat >sample.rules <<'EOF'
cat >"${rules}" <<'EOF'
GOTO="b"
LABEL="b"
LABEL="b"
EOF
cat >exp <<'EOF'
sample.rules:3 LABEL="b" is unused.
sample.rules: udev rules check failed
cat >"${exp}" <<EOF
${rules}:3 LABEL="b" is unused.
${rules}: udev rules check failed
EOF
(! udevadm verify sample.rules 2>err)
diff exp err
assert_1 "${rules}"
cat >"${rules}" <<'EOF'
GOTO="a"
LABEL="a", LABEL="b"
EOF
cat >"${exp}" <<EOF
${rules}:2 Contains multiple LABEL keys, ignoring LABEL="a".
${rules}:1 GOTO="a" has no matching label, ignoring
${rules}:1 The line takes no effect any more, dropping
${rules}:2 LABEL="b" is unused.
${rules}: udev rules check failed
EOF
assert_1 "${rules}"
# udevadm verify --root
sed "s|sample-[0-9]*.rules|${workdir}/${rules_dir}/&|" sample-*.exp >"${workdir}/${exp}"
cd -
assert_1 --root="${workdir}"
exit 0