]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udevadm-test: allow to specify extra directories to load udev rules files
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 11 Jan 2025 08:54:43 +0000 (17:54 +0900)
committerLuca Boccassi <luca.boccassi@gmail.com>
Wed, 22 Jan 2025 20:46:42 +0000 (20:46 +0000)
This adds -D/--extra-rules-dir=DIR switch for 'udevadm test' command.
When specified, udev rules files in the specified directory will be also
loaded. This may be useful for debugging udev rules by copying some udev
rules files to a temporary directory.

man/udevadm.xml
shell-completion/bash/udevadm
shell-completion/zsh/_udevadm
src/udev/test-udev-rule-runner.c
src/udev/udev-manager.c
src/udev/udev-rules.c
src/udev/udev-rules.h
src/udev/udevadm-test.c

index 01d662a0767a6c1c283bc940c5ddd847e68a0cd5..c325df911897f65d92f04aea0bf5182d96063a77 100644 (file)
             <xi:include href="version-info.xml" xpointer="v209"/>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><option>-D <replaceable>DIR</replaceable></option></term>
+          <term><option>--extra-rules-dir=<replaceable>DIR</replaceable></option></term>
+          <listitem>
+            <para>Also load udev rules files from the specified directory. This option can be specified
+            multiple times. It may be useful for debugging udev rules by copying some udev rules files to a
+            temporary directory, editing them, and specifying the directory with this option. Files found in
+            the specified directories have a higher priority than rules files in the default
+            <filename>udev/rules.d</filename> directories used by <command>systemd-udevd</command>. See
+            <citerefentry><refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum></citerefentry> for more
+            details about the search paths.</para>
+
+            <xi:include href="version-info.xml" xpointer="v258"/>
+          </listitem>
+        </varlistentry>
         <varlistentry>
           <term><option>-v</option></term>
           <term><option>--verbose</option></term>
index c8a08f68d74e75941e8df324cb9776ee3e39c6e8..63631fdaeb7d48dab535d7307eaac35fd9f69cfd 100644 (file)
@@ -70,7 +70,7 @@ _udevadm() {
         [MONITOR_STANDALONE]='-k --kernel -u --udev -p --property'
         [MONITOR_ARG]='-s --subsystem-match -t --tag-match'
         [TEST_STANDALONE]='-v --verbose'
-        [TEST_ARG]='-a --action -N --resolve-names'
+        [TEST_ARG]='-a --action -N --resolve-names -D --extra-rules-dir'
         [TEST_BUILTIN]='-a --action'
         [VERIFY]='-N --resolve-names --root --no-summary --no-style'
         [WAIT]='-t --timeout --initialized=no --removed --settle'
@@ -228,6 +228,9 @@ _udevadm() {
                     -N|--resolve-names)
                         comps='early late never'
                         ;;
+                    -D|--extra-rules-dir)
+                        comps=''
+                        compopt -o dirnames
                 esac
             elif [[ $cur = -* ]]; then
                 comps="${OPTS[COMMON]} ${OPTS[TEST_ARG]} ${OPTS[TEST_STANDALONE]}"
index 8a88aa694e02874a42da2a4f24e75b603ab43961..06aa79f47b769409aa15934eeb649d4076fa0956 100644 (file)
@@ -89,6 +89,7 @@ _udevadm_test(){
         '(-)'{-V,--version}'[Show package version]' \
         '--action=[The action string.]:actions:(add change remove move online offline bind unbind)' \
         '--subsystem=[The subsystem string.]' \
+        '(-D --extra-rules-dir=)'{-D,--extra-rules-dir=}'[Also load rules from the directory.]' \
         '(-v --verbose)'{-v,--verbose}'[Show verbose logs.]' \
         '*::devpath:_files -P /sys/ -W /sys'
 }
index 9a04abf5904b7e0b6c7620d2d7bab77e0bc0bc75..9ad446aa2d8c22f82e7b073b1271b43bea234846 100644 (file)
@@ -136,7 +136,7 @@ static int run(int argc, char *argv[]) {
                 usleep_safe(us);
         }
 
-        assert_se(udev_rules_load(&rules, RESOLVE_NAME_EARLY) == 0);
+        assert_se(udev_rules_load(&rules, RESOLVE_NAME_EARLY, /* extra = */ NULL) == 0);
 
         const char *syspath = strjoina("/sys", devpath);
         r = device_new_from_synthetic_event(&dev, syspath, action);
index 2f97205c91d052c6e265f982b43c112a2269995b..873a076a2d9f39375384dbbc5b963a03c36a3901 100644 (file)
@@ -284,7 +284,7 @@ void manager_reload(Manager *manager, bool force) {
         udev_builtin_reload(flags);
 
         if (FLAGS_SET(flags, UDEV_RELOAD_RULES)) {
-                r = udev_rules_load(&rules, manager->config.resolve_name_timing);
+                r = udev_rules_load(&rules, manager->config.resolve_name_timing, /* extra = */ NULL);
                 if (r < 0)
                         log_warning_errno(r, "Failed to read udev rules, using the previously loaded rules, ignoring: %m");
                 else
@@ -1316,7 +1316,7 @@ int manager_main(Manager *manager) {
 
         udev_builtin_init();
 
-        r = udev_rules_load(&manager->rules, manager->config.resolve_name_timing);
+        r = udev_rules_load(&manager->rules, manager->config.resolve_name_timing, /* extra = */ NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to read udev rules: %m");
 
index d94fc6fdbd3011754c0b553c2d76b782af68ac71..071b15827809e7fafb3ca4ad6b315c7301814555 100644 (file)
@@ -1778,16 +1778,26 @@ UdevRules* udev_rules_new(ResolveNameTiming resolve_name_timing) {
         return rules;
 }
 
-int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing) {
+int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing, char * const *extra) {
         _cleanup_(udev_rules_freep) UdevRules *rules = NULL;
-        _cleanup_strv_free_ char **files = NULL;
+        _cleanup_strv_free_ char **files = NULL, **directories = NULL;
         int r;
 
         rules = udev_rules_new(resolve_name_timing);
         if (!rules)
                 return -ENOMEM;
 
-        r = conf_files_list_strv(&files, ".rules", NULL, 0, RULES_DIRS);
+        if (!strv_isempty(extra)) {
+                directories = strv_copy(extra);
+                if (!directories)
+                        return -ENOMEM;
+        }
+
+        r = strv_extend_strv(&directories, CONF_PATHS_STRV("udev/rules.d"), /* filter_duplicates = */ false);
+        if (r < 0)
+                return r;
+
+        r = conf_files_list_strv(&files, ".rules", NULL, 0, (const char* const*) directories);
         if (r < 0)
                 return log_debug_errno(r, "Failed to enumerate rules files: %m");
 
index 67d7e5b178317d804969b374ddf8f6d3c5ad2cfd..62dac5ba73b1c0098eca5173ebb08b315bead574 100644 (file)
@@ -14,7 +14,7 @@ int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos, bool *
 int udev_rules_parse_file(UdevRules *rules, const char *filename, bool extra_checks, UdevRuleFile **ret);
 unsigned udev_rule_file_get_issues(UdevRuleFile *rule_file);
 UdevRules* udev_rules_new(ResolveNameTiming resolve_name_timing);
-int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing);
+int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing, char * const *extra);
 UdevRules* udev_rules_free(UdevRules *rules);
 DEFINE_TRIVIAL_CLEANUP_FUNC(UdevRules*, udev_rules_free);
 #define udev_rules_free_and_replace(a, b) free_and_replace_full(a, b, udev_rules_free)
index 5ceb6a5f289acc0265efa09a3bdec639f21de81e..c3f56d2d81b769aa1e4e90846256eb2dd6d76576 100644 (file)
@@ -10,6 +10,9 @@
 #include "sd-device.h"
 
 #include "device-private.h"
+#include "parse-argument.h"
+#include "static-destruct.h"
+#include "strv.h"
 #include "udev-builtin.h"
 #include "udev-dump.h"
 #include "udev-event.h"
 static sd_device_action_t arg_action = SD_DEVICE_ADD;
 static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY;
 static const char *arg_syspath = NULL;
+static char **arg_extra_rules_dir = NULL;
 static bool arg_verbose = false;
 
+STATIC_DESTRUCTOR_REGISTER(arg_extra_rules_dir, strv_freep);
+
 static int help(void) {
 
         printf("%s test [OPTIONS] DEVPATH\n\n"
@@ -30,6 +36,7 @@ static int help(void) {
                "  -V --version                         Show package version\n"
                "  -a --action=ACTION|help              Set action string\n"
                "  -N --resolve-names=early|late|never  When to resolve names\n"
+               "  -D --extra-rules-dir=DIR             Also load rules from the directory\n"
                "  -v --verbose                         Show verbose logs\n",
                program_invocation_short_name);
 
@@ -38,17 +45,18 @@ static int help(void) {
 
 static int parse_argv(int argc, char *argv[]) {
         static const struct option options[] = {
-                { "action",        required_argument, NULL, 'a' },
-                { "resolve-names", required_argument, NULL, 'N' },
-                { "verbose",       no_argument,       NULL, 'v' },
-                { "version",       no_argument,       NULL, 'V' },
-                { "help",          no_argument,       NULL, 'h' },
+                { "action",          required_argument, NULL, 'a' },
+                { "resolve-names",   required_argument, NULL, 'N' },
+                { "extra-rules-dir", required_argument, NULL, 'D' },
+                { "verbose",         no_argument,       NULL, 'v' },
+                { "version",         no_argument,       NULL, 'V' },
+                { "help",            no_argument,       NULL, 'h' },
                 {}
         };
 
         int r, c;
 
-        while ((c = getopt_long(argc, argv, "a:N:vVh", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "a:N:D:vVh", options, NULL)) >= 0)
                 switch (c) {
                 case 'a':
                         r = parse_device_action(optarg, &arg_action);
@@ -63,6 +71,18 @@ static int parse_argv(int argc, char *argv[]) {
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                        "--resolve-names= must be early, late or never");
                         break;
+                case 'D': {
+                        _cleanup_free_ char *p = NULL;
+
+                        r = parse_path_argument(optarg, /* suppress_root = */ false, &p);
+                        if (r < 0)
+                                return r;
+
+                        r = strv_consume(&arg_extra_rules_dir, TAKE_PTR(p));
+                        if (r < 0)
+                                return log_oom();
+                        break;
+                }
                 case 'v':
                         arg_verbose = true;
                         break;
@@ -108,7 +128,7 @@ int test_main(int argc, char *argv[], void *userdata) {
         puts("Loading builtins done.");
 
         puts("\nLoading udev rules files...");
-        r = udev_rules_load(&rules, arg_resolve_name_timing);
+        r = udev_rules_load(&rules, arg_resolve_name_timing, arg_extra_rules_dir);
         if (r < 0) {
                 log_error_errno(r, "Failed to read udev rules: %m");
                 goto out;