udevadm: introduce cat command to show udev rules (#35893)
Closes #35818.
This commit is contained in:
+81
-11
@@ -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>
|
||||
|
||||
@@ -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
|
||||
;;
|
||||
|
||||
|
||||
@@ -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,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',
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 -
|
||||
|
||||
Reference in New Issue
Block a user