udevadm: introduce cat command to show udev rules (#35893)

Closes #35818.
This commit is contained in:
Yu Watanabe
2025-01-24 01:49:42 +09:00
committed by GitHub
16 changed files with 461 additions and 141 deletions
+81 -11
View File
@@ -53,6 +53,11 @@
<arg choice="opt" rep="repeat">options</arg>
<arg choice="opt" rep="repeat"><replaceable>file</replaceable></arg>
</cmdsynopsis>
<cmdsynopsis>
<command>udevadm cat</command>
<arg choice="opt" rep="repeat">options</arg>
<arg choice="opt" rep="repeat"><replaceable>file</replaceable></arg>
</cmdsynopsis>
<cmdsynopsis>
<command>udevadm wait <optional>options</optional> <replaceable>device|syspath</replaceable></command>
</cmdsynopsis>
@@ -954,9 +959,15 @@
<para>Verify syntactic, semantic, and stylistic correctness of udev rules files.</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>Positional arguments can be used to specify one or more files to check. Each argument must be an
absolute path to a udev rules file or a directory that contains rules files, or a file name of udev
rules file (e.g. <filename>99-systemd.rules</filename>). If a file name is specified, the file will be
searched in the <filename>udev/rules.d</filename> directories that are processed by
<command>systemd-udevd</command>, and searched in the current working directory if not found. If no
files are specified, the udev rules are read from the files located in the same
<filename>udev/rules.d</filename> directories that are processed by <command>systemd-udevd</command>.
See <citerefentry><refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum></citerefentry> for more
details about the search paths.</para>
<para>The exit status is <constant>0</constant> if all specified udev
rules files are syntactically, semantically, and stylistically correct,
@@ -980,8 +991,11 @@
<varlistentry>
<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>
<para>When looking for udev rules files located in the <filename>udev/rules.d</filename>
directories, operate on files underneath the specified root path <replaceable>PATH</replaceable>.
When a file name is specified, and it is not found in the <filename>udev/rules.d</filename>
directories, the file will be searched in the specified root path
<replaceable>PATH</replaceable>, rather than the current working directory.</para>
<xi:include href="version-info.xml" xpointer="v254"/>
</listitem>
@@ -1010,6 +1024,65 @@
</variablelist>
</refsect2>
<refsect2>
<title>udevadm cat
<optional><replaceable>options</replaceable></optional>
<optional><replaceable>file</replaceable></optional>
…
</title>
<para>Show udev rules files or udev.conf.</para>
<para>Positional arguments can be used to specify one or more files to show. Each argument must be an
absolute path to a udev rules file or a directory that contains rules files, or a file name of udev
rules file (e.g. <filename>99-systemd.rules</filename>). If a file name is specified, the file will be
searched in the <filename>udev/rules.d</filename> directories that are processed by
<command>systemd-udevd</command>, and searched in the current working directory if not found. If no
files are specified, the udev rules are read from the files located in the same
<filename>udev/rules.d</filename> directories that are processed by <command>systemd-udevd</command>.
See <citerefentry><refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum></citerefentry> for more
details about the search paths.</para>
<variablelist>
<varlistentry>
<term><option>--root=<replaceable>PATH</replaceable></option></term>
<listitem>
<para>When looking for udev rules files located in the <filename>udev/rules.d</filename>
directories, operate on files underneath the specified root path <replaceable>PATH</replaceable>.
When a file name is specified, and it is not found in the <filename>udev/rules.d</filename>
directories, the file will be searched in the specified root path
<replaceable>PATH</replaceable>, rather than the current working directory.</para>
<xi:include href="version-info.xml" xpointer="v258"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--tldr</option></term>
<listitem>
<para>Only print the "interesting" parts of the configuration files, skipping comments and empty
lines and section headers followed only by comments and empty lines.</para>
<xi:include href="version-info.xml" xpointer="v258"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--config</option></term>
<listitem>
<para>Shows
<citerefentry><refentrytitle>udev.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
files, rather than udev rules files. When specified, no udev rules file cannot be specified.
</para>
<xi:include href="version-info.xml" xpointer="v258"/>
</listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
</variablelist>
</refsect2>
<refsect2>
<title>udevadm wait
<optional><replaceable>options</replaceable></optional>
@@ -1211,12 +1284,9 @@
<refsect1>
<title>See Also</title>
<para><simplelist type="inline">
<member><citerefentry>
<refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum>
</citerefentry></member>
<member><citerefentry>
<refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum>
</citerefentry></member>
<member><citerefentry><refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>udev.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
</refentry>
+68 -12
View File
@@ -46,6 +46,35 @@ __get_all_devices() {
__get_all_device_units
}
__get_root() {
local i
for ((i=0; i < COMP_CWORD; i++)); do
if [[ "${COMP_WORDS[i]}" = --root=* ]]; then
echo "${COMP_WORDS[i]#--root=}"
break
fi
if (( i > 0 )) && [[ "${COMP_WORDS[i-1]}" == "--root" ]]; then
echo "${COMP_WORDS[i]}"
break
fi
done
}
__get_udev_rules_files() {
local root=$( __get_root )
ls "$root"/usr/lib/udev/rules.d/*.rules \
"$root"/usr/local/lib/udev/rules.d/*.rules \
"$root"/run/udev/rules.d/*.rules \
"$root"/etc/udev/rules.d/*.rules 2>/dev/null
}
__get_udev_rules_names() {
local -a rules=( $( __get_udev_rules_files ) )
printf '%s\n' "${rules[@]##*/}"
}
_udevadm() {
local i verb comps builtin
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
@@ -72,13 +101,16 @@ _udevadm() {
[TEST_STANDALONE]='-v --verbose'
[TEST_ARG]='-a --action -N --resolve-names -D --extra-rules-dir'
[TEST_BUILTIN]='-a --action'
[VERIFY]='-N --resolve-names --root --no-summary --no-style'
[VERIFY_STANDALONE]='--no-summary --no-style'
[VERIFY_ARG]='-N --resolve-names --root'
[CAT_STANDALONE]='--tldr --config'
[CAT_ARG]='--root'
[WAIT]='-t --timeout --initialized=no --removed --settle'
[LOCK]='-t --timeout -d --device -b --backing -p --print'
)
local verbs=(info trigger settle control monitor test-builtin test verify wait lock)
local builtins=(blkid btrfs hwdb input_id keyboard kmod net_id net_setup_link path_id usb_id uaccess)
local verbs=(info trigger settle control monitor test-builtin test verify cat wait lock)
local builtins=(blkid btrfs hwdb input_id keyboard kmod net_driver net_id net_setup_link path_id uaccess usb_id)
for ((i=0; i < COMP_CWORD; i++)); do
if __contains_word "${COMP_WORDS[i]}" "${verbs[@]}"; then
@@ -269,27 +301,51 @@ _udevadm() {
;;
'verify')
if __contains_word "$prev" ${OPTS[VERIFY]}; then
if __contains_word "$prev" ${OPTS[VERIFY_ARG]}; then
case $prev in
-N|--resolve-names)
comps='early never'
;;
--root)
comps=$(compgen -A directory -- "$cur" )
comps=''
compopt -o dirnames
;;
*)
comps=''
;;
esac
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
return 0
fi
if [[ $cur = -* ]]; then
comps="${OPTS[COMMON]} ${OPTS[VERIFY]}"
elif [[ $cur = -* ]]; then
comps="${OPTS[COMMON]} ${OPTS[VERIFY_ARG]} ${OPTS[VERIFY_STANDALONE]}"
elif [[ $cur = */* ]]; then
comps=$( __get_udev_rules_files )
compopt -o dirnames
else
comps=$( compgen -A file -- "$cur" )
comps=$( __get_udev_rules_names )
compopt -o default
fi
;;
'cat')
if __contains_word "$prev" ${OPTS[CAT_ARG]}; then
case $prev in
--root)
comps=''
compopt -o dirnames
;;
*)
comps=''
;;
esac
elif [[ $cur = -* ]]; then
comps="${OPTS[COMMON]} ${OPTS[CAT_ARG]} ${OPTS[CAT_STANDALONE]}"
elif __contains_word "--config" ${COMP_WORDS[*]}; then
comps="${OPTS[COMMON]} ${OPTS[CAT_ARG]} ${OPTS[CAT_STANDALONE]}"
elif [[ $cur = */* ]]; then
comps=$( __get_udev_rules_files )
compopt -o dirnames
else
comps=$( __get_udev_rules_names )
compopt -o default
fi
;;
+12 -1
View File
@@ -101,7 +101,7 @@ _udevadm_test-builtin(){
'(- *)'{-h,--help}'[Print help]' \
'(- *)'{-V,--version}'[Print version of the program]' \
'--action=[The action string.]:actions:(add change remove move online offline bind unbind)' \
'*::builtins:(blkid btrfs hwdb input_id net_id net_setup_link kmod path_id usb_id uaccess)'
'*::builtins:(blkid btrfs hwdb input_id net_driver net_id net_setup_link kmod path_id uaccess usb_id)'
elif (( CURRENT == 3 )); then
_arguments \
'--action=[The action string.]:actions:(add change remove move online offline bind unbind)' \
@@ -126,6 +126,17 @@ _udevadm_verify(){
'*::files:_files'
}
(( $+functions[_udevadm_cat] )) ||
_udevadm_cat(){
_arguments \
'(- *)'{-h,--help}'[Show help]' \
'(- *)'{-V,--version}'[Show package version]' \
'--root=[Operate on catalog hierarchy under specified directory]:directories:_directories' \
--tldr'[Skip comments and empty lines.]' \
--config'[Show udev.conf.]' \
'*::files:_files'
}
(( $+functions[_udevadm_wait] )) ||
_udevadm_wait(){
_arguments \
+1
View File
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
udevadm_sources = files(
'udevadm-cat.c',
'udevadm-control.c',
'udevadm-hwdb.c',
'udevadm-info.c',
+1 -1
View File
@@ -25,10 +25,10 @@ static const UdevBuiltin *const builtins[_UDEV_BUILTIN_MAX] = {
[UDEV_BUILTIN_NET_ID] = &udev_builtin_net_id,
[UDEV_BUILTIN_NET_LINK] = &udev_builtin_net_setup_link,
[UDEV_BUILTIN_PATH_ID] = &udev_builtin_path_id,
[UDEV_BUILTIN_USB_ID] = &udev_builtin_usb_id,
#if HAVE_ACL
[UDEV_BUILTIN_UACCESS] = &udev_builtin_uaccess,
#endif
[UDEV_BUILTIN_USB_ID] = &udev_builtin_usb_id,
};
void udev_builtin_init(void) {
+1 -1
View File
@@ -47,10 +47,10 @@ extern const UdevBuiltin udev_builtin_net_driver;
extern const UdevBuiltin udev_builtin_net_id;
extern const UdevBuiltin udev_builtin_net_setup_link;
extern const UdevBuiltin udev_builtin_path_id;
extern const UdevBuiltin udev_builtin_usb_id;
#if HAVE_ACL
extern const UdevBuiltin udev_builtin_uaccess;
#endif
extern const UdevBuiltin udev_builtin_usb_id;
void udev_builtin_init(void);
void udev_builtin_exit(void);
+2 -2
View File
@@ -50,10 +50,10 @@ typedef enum UdevBuiltinCommand {
UDEV_BUILTIN_NET_ID,
UDEV_BUILTIN_NET_LINK,
UDEV_BUILTIN_PATH_ID,
UDEV_BUILTIN_USB_ID,
#if HAVE_ACL
UDEV_BUILTIN_UACCESS,
#endif
UDEV_BUILTIN_USB_ID,
_UDEV_BUILTIN_MAX,
_UDEV_BUILTIN_INVALID = -EINVAL,
} UdevBuiltinCommand;
@@ -73,10 +73,10 @@ typedef enum UdevReloadFlags {
UDEV_RELOAD_BUILTIN_NET_ID = 1u << UDEV_BUILTIN_NET_ID,
UDEV_RELOAD_BUILTIN_NET_LINK = 1u << UDEV_BUILTIN_NET_LINK,
UDEV_RELOAD_BUILTIN_PATH_ID = 1u << UDEV_BUILTIN_PATH_ID,
UDEV_RELOAD_BUILTIN_USB_ID = 1u << UDEV_BUILTIN_USB_ID,
#if HAVE_ACL
UDEV_RELOAD_BUILTIN_UACCESS = 1u << UDEV_BUILTIN_UACCESS,
#endif
UDEV_RELOAD_BUILTIN_USB_ID = 1u << UDEV_BUILTIN_USB_ID,
UDEV_RELOAD_KILL_WORKERS = 1u << (_UDEV_BUILTIN_MAX + 0),
UDEV_RELOAD_RULES = 1u << (_UDEV_BUILTIN_MAX + 1),
} UdevReloadFlags;
+8 -4
View File
@@ -1631,10 +1631,8 @@ static void udev_check_rule_line(UdevRuleLine *line) {
int udev_rules_parse_file(UdevRules *rules, const char *filename, bool extra_checks, UdevRuleFile **ret) {
_cleanup_(udev_rule_file_freep) UdevRuleFile *rule_file = NULL;
_cleanup_free_ char *continuation = NULL, *name = NULL;
_cleanup_free_ char *name = NULL;
_cleanup_fclose_ FILE *f = NULL;
bool ignore_line = false;
unsigned line_nr = 0;
struct stat st;
int r;
@@ -1685,6 +1683,9 @@ int udev_rules_parse_file(UdevRules *rules, const char *filename, bool extra_che
LIST_APPEND(rule_files, rules->rule_files, rule_file);
_cleanup_free_ char *continuation = NULL;
unsigned line_nr = 0, current_line_nr = 0;
bool ignore_line = false;
for (;;) {
_cleanup_free_ char *buf = NULL;
size_t len;
@@ -1696,7 +1697,10 @@ int udev_rules_parse_file(UdevRules *rules, const char *filename, bool extra_che
if (r == 0)
break;
line_nr++;
current_line_nr++;
if (!continuation)
line_nr = current_line_nr;
line = skip_leading_chars(buf, NULL);
/* Lines beginning with '#' are ignored regardless of line continuation. */
+110
View File
@@ -0,0 +1,110 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <getopt.h>
#include "log.h"
#include "parse-argument.h"
#include "pretty-print.h"
#include "static-destruct.h"
#include "strv.h"
#include "udevadm.h"
#include "udevadm-util.h"
static char *arg_root = NULL;
static CatFlags arg_cat_flags = 0;
static bool arg_config = false;
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
r = terminal_urlify_man("udevadm", "8", &link);
if (r < 0)
return log_oom();
printf("%s cat [OPTIONS] [FILE...]\n"
"\n%sShow udev rules files.%s\n\n"
" -h --help Show this help\n"
" -V --version Show package version\n"
" --root=PATH Operate on an alternate filesystem root\n"
" --tldr Skip comments and empty lines\n"
" --config Show udev.conf rather than udev rules files\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
ansi_normal(),
link);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_ROOT = 0x100,
ARG_TLDR,
ARG_CONFIG,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "root", required_argument, NULL, ARG_ROOT },
{ "tldr", no_argument, NULL, ARG_TLDR },
{ "config", no_argument, NULL, ARG_CONFIG },
{}
};
int r, c;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "hVN:", options, NULL)) >= 0)
switch (c) {
case 'h':
return help();
case 'V':
return print_version();
case ARG_ROOT:
r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root);
if (r < 0)
return r;
break;
case ARG_TLDR:
arg_cat_flags = CAT_TLDR;
break;
case ARG_CONFIG:
arg_config = true;
break;
case '?':
return -EINVAL;
default:
assert_not_reached();
}
if (arg_config && optind < argc)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Combination of --config and FILEs is not supported.");
return 1;
}
int cat_main(int argc, char *argv[], void *userdata) {
int r;
r = parse_argv(argc, argv);
if (r <= 0)
return r;
if (arg_config)
return conf_files_cat(arg_root, "udev/udev.conf", arg_cat_flags);
_cleanup_strv_free_ char **files = NULL;
r = search_rules_files(strv_skip(argv, optind), arg_root, &files);
if (r < 0)
return r;
/* udev rules file does not support dropin configs. So, we can safely pass multiple files as dropins. */
return cat_files(/* file = */ NULL, /* dropins = */ files, arg_cat_flags);
}
+109
View File
@@ -5,6 +5,9 @@
#include "alloc-util.h"
#include "bus-error.h"
#include "bus-util.h"
#include "chase.h"
#include "conf-files.h"
#include "constants.h"
#include "device-private.h"
#include "path-util.h"
#include "udev-ctrl.h"
@@ -168,3 +171,109 @@ int udev_ping(usec_t timeout_usec, bool ignore_connection_failure) {
return 1; /* received reply */
}
static int search_rules_file_in_conf_dirs(const char *s, const char *root, char ***files) {
_cleanup_free_ char *filename = NULL;
int r;
assert(s);
if (!endswith(s, ".rules"))
filename = strjoin(s, ".rules");
else
filename = strdup(s);
if (!filename)
return log_oom();
if (!filename_is_valid(filename))
return 0;
STRV_FOREACH(p, CONF_PATHS_STRV("udev/rules.d")) {
_cleanup_free_ char *path = NULL, *resolved = NULL;
path = path_join(*p, filename);
if (!path)
return log_oom();
r = chase(path, root, CHASE_PREFIX_ROOT | CHASE_MUST_BE_REGULAR, &resolved, /* ret_fd = */ NULL);
if (r == -ENOENT)
continue;
if (r < 0)
return log_error_errno(r, "Failed to chase \"%s\": %m", path);
r = strv_consume(files, TAKE_PTR(resolved));
if (r < 0)
return log_oom();
return 1; /* found */
}
return 0;
}
static int search_rules_file(const char *s, const char *root, char ***files) {
int r;
assert(s);
assert(files);
/* If the input is a file name (e.g. 99-systemd.rules), then try to find it in udev/rules.d directories. */
r = search_rules_file_in_conf_dirs(s, root, files);
if (r != 0)
return r;
/* If not found, or if it is a path, then chase it. */
struct stat st;
_cleanup_free_ char *resolved = NULL;
r = chase_and_stat(s, root, CHASE_PREFIX_ROOT, &resolved, &st);
if (r < 0)
return log_error_errno(r, "Failed to chase \"%s\": %m", s);
r = stat_verify_regular(&st);
if (r == -EISDIR) {
_cleanup_strv_free_ char **files_in_dir = NULL;
r = conf_files_list_strv(&files_in_dir, ".rules", root, 0, (const char* const*) STRV_MAKE_CONST(s));
if (r < 0)
return log_error_errno(r, "Failed to enumerate rules files in '%s': %m", resolved);
r = strv_extend_strv_consume(files, TAKE_PTR(files_in_dir), /* filter_duplicates = */ false);
if (r < 0)
return log_oom();
return 0;
}
if (r < 0)
return log_error_errno(r, "'%s' is neither a regular file nor a directory: %m", resolved);
r = strv_consume(files, TAKE_PTR(resolved));
if (r < 0)
return log_oom();
return 0;
}
int search_rules_files(char * const *a, const char *root, char ***ret) {
_cleanup_strv_free_ char **files = NULL;
int r;
assert(ret);
if (strv_isempty(a)) {
r = conf_files_list_strv(&files, ".rules", root, 0, (const char* const*) CONF_PATHS_STRV("udev/rules.d"));
if (r < 0)
return log_error_errno(r, "Failed to enumerate rules files: %m");
if (root && strv_isempty(files))
return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No rules files found in %s.", root);
} else
STRV_FOREACH(s, a) {
r = search_rules_file(*s, root, &files);
if (r < 0)
return r;
}
*ret = TAKE_PTR(files);
return 0;
}
+1
View File
@@ -7,3 +7,4 @@ int find_device(const char *id, const char *prefix, sd_device **ret);
int find_device_with_action(const char *id, sd_device_action_t action, sd_device **ret);
int parse_device_action(const char *str, sd_device_action_t *action);
int udev_ping(usec_t timeout, bool ignore_connection_failure);
int search_rules_files(char * const *a, const char *root, char ***ret);
+17 -68
View File
@@ -7,16 +7,15 @@
#include <stdlib.h>
#include <unistd.h>
#include "conf-files.h"
#include "constants.h"
#include "errno-util.h"
#include "log.h"
#include "parse-argument.h"
#include "pretty-print.h"
#include "stat-util.h"
#include "static-destruct.h"
#include "strv.h"
#include "udev-rules.h"
#include "udevadm.h"
#include "udevadm-util.h"
static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY;
static char *arg_root = NULL;
@@ -109,10 +108,6 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached();
}
if (arg_root && optind < argc)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Combination of --root= and FILEs is not supported.");
return 1;
}
@@ -139,57 +134,20 @@ static int verify_rules_file(UdevRules *rules, const char *fname) {
return 0;
}
static int verify_rules_filelist(UdevRules *rules, char **files, size_t *fail_count, size_t *success_count, bool walk_dirs);
static int verify_rules_dir(UdevRules *rules, const char *dir, size_t *fail_count, size_t *success_count) {
int r;
_cleanup_strv_free_ char **files = NULL;
assert(rules);
assert(dir);
assert(fail_count);
assert(success_count);
r = conf_files_list(&files, ".rules", NULL, 0, dir);
if (r < 0)
return log_error_errno(r, "Failed to enumerate rules files: %m");
return verify_rules_filelist(rules, files, fail_count, success_count, /* walk_dirs */ false);
}
static int verify_rules_filelist(UdevRules *rules, char **files, size_t *fail_count, size_t *success_count, bool walk_dirs) {
int r, rv = 0;
assert(rules);
assert(files);
assert(fail_count);
assert(success_count);
STRV_FOREACH(fp, files) {
if (walk_dirs && is_dir(*fp, /* follow = */ true) > 0)
r = verify_rules_dir(rules, *fp, fail_count, success_count);
else {
r = verify_rules_file(rules, *fp);
if (r < 0)
++(*fail_count);
else
++(*success_count);
}
if (r < 0 && rv >= 0)
rv = r;
}
return rv;
}
static int verify_rules(UdevRules *rules, char **files) {
size_t fail_count = 0, success_count = 0;
int r;
int r, ret = 0;
assert(rules);
assert(files);
r = verify_rules_filelist(rules, files, &fail_count, &success_count, /* walk_dirs */ true);
STRV_FOREACH(fp, files) {
r = verify_rules_file(rules, *fp);
if (r < 0)
++fail_count;
else
++success_count;
RET_GATHER(ret, r);
}
if (arg_summary)
printf("\n%s%zu udev rules files have been checked.%s\n"
@@ -203,7 +161,7 @@ static int verify_rules(UdevRules *rules, char **files) {
fail_count,
fail_count > 0 ? ansi_normal() : "");
return r;
return ret;
}
int verify_main(int argc, char *argv[], void *userdata) {
@@ -218,19 +176,10 @@ 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;
_cleanup_strv_free_ char **files = NULL;
r = search_rules_files(strv_skip(argv, optind), arg_root, &files);
if (r < 0)
return r;
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));
return verify_rules(rules, files);
}
+2
View File
@@ -26,6 +26,7 @@ static int help(void) {
{ "test", "Test an event run" },
{ "test-builtin", "Test a built-in command" },
{ "verify", "Verify udev rules files" },
{ "cat", "Show udev rules files" },
{ "wait", "Wait for device or device symlink" },
{ "lock", "Lock a block device" },
};
@@ -97,6 +98,7 @@ static int help_main(int argc, char *argv[], void *userdata) {
static int udevadm_main(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "cat", VERB_ANY, VERB_ANY, 0, cat_main },
{ "info", VERB_ANY, VERB_ANY, 0, info_main },
{ "trigger", VERB_ANY, VERB_ANY, 0, trigger_main },
{ "settle", VERB_ANY, VERB_ANY, 0, settle_main },
+1
View File
@@ -5,6 +5,7 @@
#include "macro.h"
int cat_main(int argc, char *argv[], void *userdata);
int info_main(int argc, char *argv[], void *userdata);
int trigger_main(int argc, char *argv[], void *userdata);
int settle_main(int argc, char *argv[], void *userdata);
+9
View File
@@ -33,6 +33,15 @@ udevadm settle --timeout 30
udevadm -h
udevadm cat
udevadm cat 99-systemd
udevadm cat 99-systemd.rules
udevadm cat /usr/lib/udev/rules.d/99-systemd.rules
udevadm cat /usr/lib/udev/rules.d
(! udevadm cat /dev/null)
udevadm cat --config
udevadm cat -h
INVOCATION_ID=$(systemctl show --property InvocationID --value systemd-udevd.service)
udevadm control -e
# Wait for systemd-udevd.service being restarted.
+38 -41
View File
@@ -8,6 +8,8 @@ set -o pipefail
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
PATH=/var/build:$PATH
# shellcheck disable=SC2317
cleanup() {
cd /
@@ -101,7 +103,6 @@ assert_0 -h
assert_0 --help
assert_0 -V
assert_0 --version
assert_0 /dev/null
# unrecognized option '--unknown'
assert_1 --unknown
@@ -116,13 +117,9 @@ assert_1 --resolve-names=now
# Failed to parse rules file ./nosuchfile: No such file or directory
assert_1 ./nosuchfile
# Failed to parse rules file ./nosuchfile: No such file or directory
cat >"${exo}" <<EOF
3 udev rules files have been checked.
Success: 2
Fail: 1
EOF
assert_1 /dev/null ./nosuchfile /dev/null
assert_1 ./nosuchfile /dev/null
# '/dev/null' is neither a regular file nor a directory: File descriptor in bad state
assert_1 /dev/null
rules_dir='etc/udev/rules.d'
mkdir -p "${rules_dir}"
@@ -148,8 +145,6 @@ assert_0 --root="${workdir}" --no-summary
cp "${workdir}/default_output_1_success" "${exo}"
assert_0 "${rules_dir}"
# Combination of --root= and FILEs is not supported.
assert_1 --root="${workdir}" /dev/null
# No rules files found in nosuchdir
assert_1 --root=nosuchdir
@@ -161,7 +156,7 @@ assert_0 "${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}"
echo "Failed to parse rules file $(pwd)/${rules}: No buffer space available" >"${exp}"
assert_1 "${rules}"
{
@@ -174,17 +169,17 @@ assert_0 "${rules}"
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.
$(pwd)/${rules}:1 Line is too long, ignored.
$(pwd)/${rules}: udev rules check failed.
EOF
assert_1 "${rules}"
printf '\\\n' >"${rules}"
cat >"${exp}" <<EOF
${rules}:1 Unexpected EOF after line continuation, line ignored.
${rules}: udev rules check failed.
$(pwd)/${rules}:1 Unexpected EOF after line continuation, line ignored.
$(pwd)/${rules}: udev rules check failed.
EOF
assert_1 "${rules}"
assert_1 --root="${workdir}" "${rules}"
test_syntax_error() {
local rule msg
@@ -194,8 +189,8 @@ test_syntax_error() {
printf '%s\n' "${rule}" >"${rules}"
cat >"${exp}" <<EOF
${rules}:1 ${msg}
${rules}: udev rules check failed.
$(pwd)/${rules}:1 ${msg}
$(pwd)/${rules}: udev rules check failed.
EOF
assert_1 "${rules}"
}
@@ -208,8 +203,8 @@ test_style_error() {
printf '%s\n' "${rule}" >"${rules}"
cat >"${exp}" <<EOF
${rules}:1 ${msg}
${rules}: udev rules have style issues.
$(pwd)/${rules}:1 ${msg}
$(pwd)/${rules}: udev rules have style issues.
EOF
assert_0_impl --no-style "${rules}"
assert_1_impl "${rules}"
@@ -365,9 +360,9 @@ assert_0 "${rules}"
echo 'GOTO="a"' >"${rules}"
cat >"${exp}" <<EOF
${rules}:1 GOTO="a" has no matching label, ignoring.
${rules}:1 The line has no effect any more, dropping.
${rules}: udev rules check failed.
$(pwd)/${rules}:1 GOTO="a" has no matching label, ignoring.
$(pwd)/${rules}:1 The line has no effect any more, dropping.
$(pwd)/${rules}: udev rules check failed.
EOF
assert_1 "${rules}"
@@ -383,8 +378,8 @@ LABEL="b"
LABEL="b"
EOF
cat >"${exp}" <<EOF
${rules}:3 style: LABEL="b" is unused.
${rules}: udev rules have style issues.
$(pwd)/${rules}:3 style: LABEL="b" is unused.
$(pwd)/${rules}: udev rules have style issues.
EOF
assert_0_impl --no-style "${rules}"
assert_1_impl "${rules}"
@@ -394,11 +389,11 @@ 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 has no effect any more, dropping.
${rules}:2 style: LABEL="b" is unused.
${rules}: udev rules check failed.
$(pwd)/${rules}:2 Contains multiple LABEL keys, ignoring LABEL="a".
$(pwd)/${rules}:1 GOTO="a" has no matching label, ignoring.
$(pwd)/${rules}:1 The line has no effect any more, dropping.
$(pwd)/${rules}:2 style: LABEL="b" is unused.
$(pwd)/${rules}: udev rules check failed.
EOF
assert_1 "${rules}"
@@ -406,9 +401,9 @@ cat >"${rules}" <<'EOF'
KERNEL!="", KERNEL=="?*", KERNEL=="", NAME="a"
EOF
cat >"${exp}" <<EOF
${rules}:1 duplicate expressions.
${rules}:1 conflicting match expressions, the line has no effect.
${rules}: udev rules check failed.
$(pwd)/${rules}:1 duplicate expressions.
$(pwd)/${rules}:1 conflicting match expressions, the line has no effect.
$(pwd)/${rules}: udev rules check failed.
EOF
assert_1 "${rules}"
@@ -416,9 +411,9 @@ cat >"${rules}" <<'EOF'
ACTION=="a"NAME="b"
EOF
cat >"${exp}" <<EOF
${rules}:1 style: a comma between tokens is expected.
${rules}:1 style: whitespace between tokens is expected.
${rules}: udev rules have style issues.
$(pwd)/${rules}:1 style: a comma between tokens is expected.
$(pwd)/${rules}:1 style: whitespace between tokens is expected.
$(pwd)/${rules}: udev rules have style issues.
EOF
assert_0_impl --no-style "${rules}"
assert_1_impl "${rules}"
@@ -428,22 +423,24 @@ cat >"${rules}" <<'EOF'
ACTION=="a" ,NAME="b"
EOF
cat >"${exp}" <<EOF
${rules}:1 style: stray whitespace before comma.
${rules}:1 style: whitespace after comma is expected.
${rules}: udev rules have style issues.
$(pwd)/${rules}:1 style: stray whitespace before comma.
$(pwd)/${rules}:1 style: whitespace after comma is expected.
$(pwd)/${rules}: udev rules have style issues.
EOF
assert_0_impl --no-style "${rules}"
assert_1_impl "${rules}"
next_test_number
# udevadm verify --root
sed "s|sample-[0-9]*.rules|${workdir}/${rules_dir}/&|" sample-*.exp >"${workdir}/${exp}"
#sed "s|sample-[0-9]*.rules|${workdir}/${rules_dir}/&|" sample-*.exp >"${workdir}/${exp}"
cat sample-*.exp >"${workdir}/${exp}"
cd -
assert_1 --root="${workdir}"
cd -
# udevadm verify path/
sed "s|sample-[0-9]*.rules|${workdir}/${rules_dir}/&|" sample-*.exp >"${workdir}/${exp}"
#sed "s|sample-[0-9]*.rules|${workdir}/${rules_dir}/&|" sample-*.exp >"${workdir}/${exp}"
cat sample-*.exp >"${workdir}/${exp}"
cd -
assert_1 "${rules_dir}"
cd -