diff --git a/man/udevadm.xml b/man/udevadm.xml
index c325df9118..507ab49b48 100644
--- a/man/udevadm.xml
+++ b/man/udevadm.xml
@@ -53,6 +53,11 @@
options
file
+
+ udevadm cat
+ options
+ file
+
udevadm wait options device|syspath
@@ -954,9 +959,15 @@
Verify syntactic, semantic, and stylistic correctness of udev rules files.
- 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.
+ 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. 99-systemd.rules). If a file name is specified, the file will be
+ searched in the udev/rules.d directories that are processed by
+ systemd-udevd, 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
+ udev/rules.d directories that are processed by systemd-udevd.
+ See udev7 for more
+ details about the search paths.
The exit status is 0 if all specified udev
rules files are syntactically, semantically, and stylistically correct,
@@ -980,8 +991,11 @@
- When looking for udev rules files located in udev/rules.d directories,
- operate on files underneath the specified root path PATH.
+ When looking for udev rules files located in the udev/rules.d
+ directories, operate on files underneath the specified root path PATH.
+ When a file name is specified, and it is not found in the udev/rules.d
+ directories, the file will be searched in the specified root path
+ PATH, rather than the current working directory.
@@ -1010,6 +1024,65 @@
+
+ udevadm cat
+ options
+ file
+ …
+
+
+ Show udev rules files or udev.conf.
+
+ 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. 99-systemd.rules). If a file name is specified, the file will be
+ searched in the udev/rules.d directories that are processed by
+ systemd-udevd, 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
+ udev/rules.d directories that are processed by systemd-udevd.
+ See udev7 for more
+ details about the search paths.
+
+
+
+
+
+ When looking for udev rules files located in the udev/rules.d
+ directories, operate on files underneath the specified root path PATH.
+ When a file name is specified, and it is not found in the udev/rules.d
+ directories, the file will be searched in the specified root path
+ PATH, rather than the current working directory.
+
+
+
+
+
+
+
+
+ Only print the "interesting" parts of the configuration files, skipping comments and empty
+ lines and section headers followed only by comments and empty lines.
+
+
+
+
+
+
+
+
+ Shows
+ udev.conf5
+ files, rather than udev rules files. When specified, no udev rules file cannot be specified.
+
+
+
+
+
+
+
+
+
+
udevadm wait
options
@@ -1211,12 +1284,9 @@
See Also
-
- udev7
-
-
- systemd-udevd.service8
-
+ udev7
+ systemd-udevd.service8
+ udev.conf5
diff --git a/shell-completion/bash/udevadm b/shell-completion/bash/udevadm
index 63631fdaeb..02a025ff45 100644
--- a/shell-completion/bash/udevadm
+++ b/shell-completion/bash/udevadm
@@ -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
;;
diff --git a/shell-completion/zsh/_udevadm b/shell-completion/zsh/_udevadm
index 06aa79f47b..7572b09f67 100644
--- a/shell-completion/zsh/_udevadm
+++ b/shell-completion/zsh/_udevadm
@@ -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 \
diff --git a/src/udev/meson.build b/src/udev/meson.build
index 8b40e02a8b..697d108141 100644
--- a/src/udev/meson.build
+++ b/src/udev/meson.build
@@ -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',
diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c
index b190504a83..749589463d 100644
--- a/src/udev/udev-builtin.c
+++ b/src/udev/udev-builtin.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) {
diff --git a/src/udev/udev-builtin.h b/src/udev/udev-builtin.h
index 3b5f3bd120..83cf103ab5 100644
--- a/src/udev/udev-builtin.h
+++ b/src/udev/udev-builtin.h
@@ -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);
diff --git a/src/udev/udev-def.h b/src/udev/udev-def.h
index ed231764bc..c157c487cf 100644
--- a/src/udev/udev-def.h
+++ b/src/udev/udev-def.h
@@ -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;
diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
index 6c5c0f4533..29c8c37c29 100644
--- a/src/udev/udev-rules.c
+++ b/src/udev/udev-rules.c
@@ -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. */
diff --git a/src/udev/udevadm-cat.c b/src/udev/udevadm-cat.c
new file mode 100644
index 0000000000..2d7e86994d
--- /dev/null
+++ b/src/udev/udevadm-cat.c
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include
+
+#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);
+}
diff --git a/src/udev/udevadm-util.c b/src/udev/udevadm-util.c
index 2a3e974d04..641adb4f97 100644
--- a/src/udev/udevadm-util.c
+++ b/src/udev/udevadm-util.c
@@ -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;
+}
diff --git a/src/udev/udevadm-util.h b/src/udev/udevadm-util.h
index 4b58efbe97..0a8b31ada7 100644
--- a/src/udev/udevadm-util.h
+++ b/src/udev/udevadm-util.h
@@ -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);
diff --git a/src/udev/udevadm-verify.c b/src/udev/udevadm-verify.c
index 32202508f3..fb8cdee4f2 100644
--- a/src/udev/udevadm-verify.c
+++ b/src/udev/udevadm-verify.c
@@ -7,16 +7,15 @@
#include
#include
-#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);
}
diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c
index 30b6ddb728..ebf1e5190c 100644
--- a/src/udev/udevadm.c
+++ b/src/udev/udevadm.c
@@ -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 },
diff --git a/src/udev/udevadm.h b/src/udev/udevadm.h
index 7920a70d5b..e39dbf655d 100644
--- a/src/udev/udevadm.h
+++ b/src/udev/udevadm.h
@@ -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);
diff --git a/test/units/TEST-17-UDEV.10.sh b/test/units/TEST-17-UDEV.10.sh
index 8643e9e6bb..b8f5648a47 100755
--- a/test/units/TEST-17-UDEV.10.sh
+++ b/test/units/TEST-17-UDEV.10.sh
@@ -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.
diff --git a/test/units/TEST-17-UDEV.11.sh b/test/units/TEST-17-UDEV.11.sh
index 8413d3c189..ac99a80b0f 100755
--- a/test/units/TEST-17-UDEV.11.sh
+++ b/test/units/TEST-17-UDEV.11.sh
@@ -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}" <"${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}" <"${rules}"
cat >"${exp}" <"${rules}"
cat >"${exp}" <"${rules}"
cat >"${exp}" <"${rules}"
cat >"${exp}" <"${exp}" <"${exp}" <"${rules}" <<'EOF'
KERNEL!="", KERNEL=="?*", KERNEL=="", NAME="a"
EOF
cat >"${exp}" <"${rules}" <<'EOF'
ACTION=="a"NAME="b"
EOF
cat >"${exp}" <"${rules}" <<'EOF'
ACTION=="a" ,NAME="b"
EOF
cat >"${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 -