Merge pull request #26739 from ldv-alt/udevadm-verify
udevadm verify: introduce --root option
This commit is contained in:
+10
-3
@@ -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" />
|
||||
|
||||
@@ -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=''
|
||||
;;
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user