]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udevadm-info: split arg. parsing into a separate function
authorDavid Tardon <dtardon@redhat.com>
Fri, 3 Nov 2023 13:56:52 +0000 (14:56 +0100)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 9 May 2025 03:14:16 +0000 (12:14 +0900)
src/udev/udevadm-info.c

index 9b72977bcae26f96bcec04491f4ca0a02571b6ae..2eda70d1c01e7fae1afe086f7dfdac34f5634ce9 100644 (file)
@@ -42,6 +42,7 @@ typedef enum ActionType {
         ACTION_DEVICE_ID_FILE,
         ACTION_TREE,
         ACTION_EXPORT,
+        ACTION_CLEANUP_DB,
 } ActionType;
 
 typedef enum QueryType {
@@ -60,8 +61,33 @@ static const char *arg_export_prefix = NULL;
 static usec_t arg_wait_for_initialization_timeout = 0;
 static PagerFlags arg_pager_flags = 0;
 static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
+static ActionType arg_action_type = ACTION_QUERY;
+static QueryType arg_query = QUERY_ALL;
+static char **arg_devices = NULL;
+static char *arg_name = NULL;
+static char **arg_attr_match = NULL;
+static char **arg_attr_nomatch = NULL;
+static char **arg_name_match = NULL;
+static char **arg_parent_match = NULL;
+static char **arg_property_match = NULL;
+static char **arg_subsystem_match = NULL;
+static char **arg_subsystem_nomatch = NULL;
+static char **arg_sysname_match = NULL;
+static char **arg_tag_match = NULL;
+static int arg_initialized_match = -1;
 
 STATIC_DESTRUCTOR_REGISTER(arg_properties, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_devices, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_name, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_attr_match, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_attr_nomatch, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_name_match, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_parent_match, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_property_match, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_subsystem_match, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_subsystem_nomatch, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_sysname_match, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_tag_match, strv_freep);
 
 /* Put a limit on --tree descent level to not exhaust our stack */
 #define TREE_DEPTH_MAX 64
@@ -444,12 +470,127 @@ static int stat_device(const char *name, bool export, const char *prefix) {
         return 0;
 }
 
-static int export_devices(sd_device_enumerator *e) {
-        sd_device *d;
+static int add_match_parent(sd_device_enumerator *e, const char *s, const char *prefix) {
+        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+        int r;
+
+        assert(e);
+        assert(s);
+
+        r = find_device(s, prefix, &dev);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open the device '%s': %m", s);
+
+        r = device_enumerator_add_match_parent_incremental(e, dev);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add parent match '%s': %m", s);
+
+        return 0;
+}
+
+static int setup_matches(sd_device_enumerator *e) {
         int r;
 
         assert(e);
 
+        STRV_FOREACH(n, arg_name_match) {
+                r = add_match_parent(e, *n, "/dev");
+                if (r < 0)
+                        return r;
+        }
+
+        STRV_FOREACH(p, arg_parent_match) {
+                r = add_match_parent(e, *p, "/sys");
+                if (r < 0)
+                        return r;
+        }
+
+        STRV_FOREACH(s, arg_subsystem_match) {
+                r = sd_device_enumerator_add_match_subsystem(e, *s, /* match= */ true);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add subsystem match '%s': %m", *s);
+        }
+
+        STRV_FOREACH(s, arg_subsystem_nomatch) {
+                r = sd_device_enumerator_add_match_subsystem(e, *s, /* match= */ false);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add negative subsystem match '%s': %m", *s);
+        }
+
+        STRV_FOREACH(a, arg_attr_match) {
+                _cleanup_free_ char *k = NULL, *v = NULL;
+
+                r = parse_key_value_argument(*a, /* require_value= */ true, &k, &v);
+                if (r < 0)
+                        return r;
+
+                r = sd_device_enumerator_add_match_sysattr(e, k, v, /* match= */ true);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add sysattr match '%s=%s': %m", k, v);
+        }
+
+        STRV_FOREACH(a, arg_attr_nomatch) {
+                _cleanup_free_ char *k = NULL, *v = NULL;
+
+                r = parse_key_value_argument(*a, /* require_value= */ true, &k, &v);
+                if (r < 0)
+                        return r;
+
+                r = sd_device_enumerator_add_match_sysattr(e, k, v, /* match= */ false);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add negative sysattr match '%s=%s': %m", k, v);
+        }
+
+        STRV_FOREACH(p, arg_property_match) {
+                _cleanup_free_ char *k = NULL, *v = NULL;
+
+                r = parse_key_value_argument(*p, /* require_value= */ true, &k, &v);
+                if (r < 0)
+                        return r;
+
+                r = sd_device_enumerator_add_match_property_required(e, k, v);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add property match '%s=%s': %m", k, v);
+        }
+
+        STRV_FOREACH(t, arg_tag_match) {
+                r = sd_device_enumerator_add_match_tag(e, *t);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add tag match '%s': %m", *t);
+        }
+
+        STRV_FOREACH(s, arg_sysname_match) {
+                r = sd_device_enumerator_add_match_sysname(e, *s);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add sysname match '%s': %m", *s);
+        }
+
+        if (arg_initialized_match != -1) {
+                r = device_enumerator_add_match_is_initialized(e, arg_initialized_match);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set initialized filter: %m");
+        }
+
+        return 0;
+}
+
+static int export_devices(void) {
+        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+        sd_device *d;
+        int r;
+
+        r = sd_device_enumerator_new(&e);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create device enumerator: %m");
+
+        r = sd_device_enumerator_allow_uninitialized(e);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allow uninitialized devices: %m");
+
+        r = setup_matches(e);
+        if (r < 0)
+                return r;
+
         r = device_enumerator_scan_devices(e);
         if (r < 0)
                 return log_error_errno(r, "Failed to scan devices: %m");
@@ -854,30 +995,7 @@ static int print_tree(sd_device* below) {
         return 0;
 }
 
-static int ensure_device_enumerator(sd_device_enumerator **e) {
-        int r;
-
-        assert(e);
-
-        if (*e)
-                return 0;
-
-        r = sd_device_enumerator_new(e);
-        if (r < 0)
-                return log_error_errno(r, "Failed to create device enumerator: %m");
-
-        r = sd_device_enumerator_allow_uninitialized(*e);
-        if (r < 0)
-                return log_error_errno(r, "Failed to allow uninitialized devices: %m");
-
-        return 0;
-}
-
-int info_main(int argc, char *argv[], void *userdata) {
-        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
-        _cleanup_strv_free_ char **devices = NULL;
-        _cleanup_free_ char *name = NULL;
-        int c, r, ret;
+static int parse_argv(int argc, char *argv[]) {
 
         enum {
                 ARG_PROPERTY = 0x100,
@@ -930,11 +1048,14 @@ int info_main(int argc, char *argv[], void *userdata) {
                 {}
         };
 
-        ActionType action = ACTION_QUERY;
-        QueryType query = QUERY_ALL;
+        int c, r;
+
+        assert(argc >= 0);
+        assert(argv);
 
         while ((c = getopt_long(argc, argv, "atced:n:p:q:rxP:w::Vh", options, NULL)) >= 0)
                 switch (c) {
+
                 case ARG_PROPERTY:
                         /* Make sure that if the empty property list was specified, we won't show any
                            properties. */
@@ -948,9 +1069,11 @@ int info_main(int argc, char *argv[], void *userdata) {
                                         return log_oom();
                         }
                         break;
+
                 case ARG_VALUE:
                         arg_value = true;
                         break;
+
                 case 'n':
                 case 'p': {
                         const char *prefix = c == 'n' ? "/dev/" : "/sys/";
@@ -960,55 +1083,64 @@ int info_main(int argc, char *argv[], void *userdata) {
                         if (!path)
                                 return log_oom();
 
-                        r = strv_consume(&devices, path);
+                        r = strv_consume(&arg_devices, path);
                         if (r < 0)
                                 return log_oom();
                         break;
                 }
 
                 case 'q':
-                        action = ACTION_QUERY;
+                        arg_action_type = ACTION_QUERY;
                         if (streq(optarg, "property") || streq(optarg, "env"))
-                                query = QUERY_PROPERTY;
+                                arg_query = QUERY_PROPERTY;
                         else if (streq(optarg, "name"))
-                                query = QUERY_NAME;
+                                arg_query = QUERY_NAME;
                         else if (streq(optarg, "symlink"))
-                                query = QUERY_SYMLINK;
+                                arg_query = QUERY_SYMLINK;
                         else if (streq(optarg, "path"))
-                                query = QUERY_PATH;
+                                arg_query = QUERY_PATH;
                         else if (streq(optarg, "all"))
-                                query = QUERY_ALL;
+                                arg_query = QUERY_ALL;
                         else
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "unknown query type");
                         break;
+
                 case 'r':
                         arg_root = true;
                         break;
+
                 case 'd':
-                        action = ACTION_DEVICE_ID_FILE;
-                        r = free_and_strdup(&name, optarg);
+                        arg_action_type = ACTION_DEVICE_ID_FILE;
+                        r = free_and_strdup(&arg_name, optarg);
                         if (r < 0)
                                 return log_oom();
                         break;
+
                 case 'a':
-                        action = ACTION_ATTRIBUTE_WALK;
+                        arg_action_type = ACTION_ATTRIBUTE_WALK;
                         break;
+
                 case 't':
-                        action = ACTION_TREE;
+                        arg_action_type = ACTION_TREE;
                         break;
+
                 case 'e':
-                        action = ACTION_EXPORT;
+                        arg_action_type = ACTION_EXPORT;
                         break;
+
                 case 'c':
-                        cleanup_db();
-                        return 0;
+                        arg_action_type = ACTION_CLEANUP_DB;
+                        break;
+
                 case 'x':
                         arg_export = true;
                         break;
+
                 case 'P':
                         arg_export = true;
                         arg_export_prefix = optarg;
                         break;
+
                 case 'w':
                         if (optarg) {
                                 r = parse_sec(optarg, &arg_wait_for_initialization_timeout);
@@ -1017,10 +1149,13 @@ int info_main(int argc, char *argv[], void *userdata) {
                         } else
                                 arg_wait_for_initialization_timeout = USEC_INFINITY;
                         break;
+
                 case 'V':
                         return print_version();
+
                 case 'h':
                         return help();
+
                 case ARG_NO_PAGER:
                         arg_pager_flags |= PAGER_DISABLE;
                         break;
@@ -1032,133 +1167,101 @@ int info_main(int argc, char *argv[], void *userdata) {
                         break;
 
                 case ARG_SUBSYSTEM_MATCH:
-                case ARG_SUBSYSTEM_NOMATCH:
-                        r = ensure_device_enumerator(&e);
+                        r = strv_extend(&arg_subsystem_match, optarg);
                         if (r < 0)
-                                return r;
+                                return log_oom();
+                        break;
 
-                        r = sd_device_enumerator_add_match_subsystem(e, optarg, c == ARG_SUBSYSTEM_MATCH);
+                case ARG_SUBSYSTEM_NOMATCH:
+                        r = strv_extend(&arg_subsystem_nomatch, optarg);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to add%s subsystem match '%s': %m",
-                                                       c == ARG_SUBSYSTEM_MATCH ? "" : " negative", optarg);
-
+                                return log_oom();
                         break;
 
                 case ARG_ATTR_MATCH:
-                case ARG_ATTR_NOMATCH: {
-                        _cleanup_free_ char *k = NULL, *v = NULL;
-
-                        r = ensure_device_enumerator(&e);
-                        if (r < 0)
-                                return r;
-
-                        r = parse_key_value_argument(optarg, /* require_value= */ true, &k, &v);
-                        if (r < 0)
-                                return r;
+                        if (!strchr(optarg, '='))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                "expect <ATTR>=<value> instead of '%s'", optarg);
 
-                        r = sd_device_enumerator_add_match_sysattr(e, k, v, c == ARG_ATTR_MATCH);
+                        r = strv_extend(&arg_attr_match, optarg);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to add%s sysattr match '%s=%s': %m",
-                                                       c == ARG_ATTR_MATCH ? "" : " negative", k, v);
+                                return log_oom();
                         break;
-                }
 
-                case ARG_PROPERTY_MATCH: {
-                        _cleanup_free_ char *k = NULL, *v = NULL;
+                case ARG_ATTR_NOMATCH:
+                        if (!strchr(optarg, '='))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                "expect <ATTR>=<value> instead of '%s'", optarg);
 
-                        r = ensure_device_enumerator(&e);
+                        r = strv_extend(&arg_attr_nomatch, optarg);
                         if (r < 0)
-                                return r;
+                                return log_oom();
+                        break;
 
-                        r = parse_key_value_argument(optarg, /* require_value= */ true, &k, &v);
-                        if (r < 0)
-                                return r;
+                case ARG_PROPERTY_MATCH:
+                        if (!strchr(optarg, '='))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                "expect <PROPERTY>=<value> instead of '%s'", optarg);
 
-                        r = sd_device_enumerator_add_match_property_required(e, k, v);
+                        r = strv_extend(&arg_property_match, optarg);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to add property match '%s=%s': %m", k, v);
+                                return log_oom();
                         break;
-                }
 
                 case ARG_TAG_MATCH:
-                        r = ensure_device_enumerator(&e);
+                        r = strv_extend(&arg_tag_match, optarg);
                         if (r < 0)
-                                return r;
-
-                        r = sd_device_enumerator_add_match_tag(e, optarg);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to add tag match '%s': %m", optarg);
+                                return log_oom();
                         break;
 
                 case ARG_SYSNAME_MATCH:
-                        r = ensure_device_enumerator(&e);
-                        if (r < 0)
-                                return r;
-
-                        r = sd_device_enumerator_add_match_sysname(e, optarg);
+                        r = strv_extend(&arg_sysname_match, optarg);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to add sysname match '%s': %m", optarg);
+                                return log_oom();
                         break;
 
                 case ARG_NAME_MATCH:
-                case ARG_PARENT_MATCH: {
-                        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
-
-                        r = find_device(optarg, c == ARG_NAME_MATCH ? "/dev" : "/sys", &dev);
+                        r = strv_extend(&arg_name_match, optarg);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to open the device '%s': %m", optarg);
-
-                        r = ensure_device_enumerator(&e);
-                        if (r < 0)
-                                return r;
+                                return log_oom();
+                        break;
 
-                        r = device_enumerator_add_match_parent_incremental(e, dev);
+                case ARG_PARENT_MATCH:
+                        r = strv_extend(&arg_parent_match, optarg);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to add parent match '%s': %m", optarg);
+                                return log_oom();
                         break;
-                }
 
                 case ARG_INITIALIZED_MATCH:
-                case ARG_INITIALIZED_NOMATCH:
-                        r = ensure_device_enumerator(&e);
-                        if (r < 0)
-                                return r;
+                        arg_initialized_match = MATCH_INITIALIZED_YES;
+                        break;
 
-                        r = device_enumerator_add_match_is_initialized(e, c == ARG_INITIALIZED_MATCH ? MATCH_INITIALIZED_YES : MATCH_INITIALIZED_NO);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to set initialized filter: %m");
+                case ARG_INITIALIZED_NOMATCH:
+                        arg_initialized_match = MATCH_INITIALIZED_NO;
                         break;
 
                 case '?':
                         return -EINVAL;
+
                 default:
                         assert_not_reached();
                 }
 
-        if (action == ACTION_DEVICE_ID_FILE) {
-                if (argv[optind])
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                               "Positional arguments are not allowed with -d/--device-id-of-file.");
-                assert(name);
-                return stat_device(name, arg_export, arg_export_prefix);
-        }
-
-        if (action == ACTION_EXPORT) {
-                r = ensure_device_enumerator(&e);
-                if (r < 0)
-                        return r;
-
-                return export_devices(e);
-        }
-
-        r = strv_extend_strv(&devices, argv + optind, false);
+        r = strv_extend_strv(&arg_devices, argv + optind, /* filter_duplicates= */ false);
         if (r < 0)
                 return log_error_errno(r, "Failed to build argument list: %m");
+        bool has_positional_args = r > 0;
+
+        if (IN_SET(arg_action_type, ACTION_DEVICE_ID_FILE, ACTION_CLEANUP_DB) && has_positional_args)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Positional arguments are not allowed with -d/--device-id-of-file and -c/--cleanup-db.");
 
-        if (action != ACTION_TREE && strv_isempty(devices))
+        if (!IN_SET(arg_action_type, ACTION_DEVICE_ID_FILE, ACTION_CLEANUP_DB, ACTION_EXPORT, ACTION_TREE) &&
+            strv_isempty(arg_devices))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "A device name or path is required");
-        if (IN_SET(action, ACTION_ATTRIBUTE_WALK, ACTION_TREE) && strv_length(devices) > 1)
+
+        if (IN_SET(arg_action_type, ACTION_ATTRIBUTE_WALK, ACTION_TREE) && strv_length(arg_devices) > 1)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Only one device may be specified with -a/--attribute-walk and -t/--tree");
 
@@ -1166,15 +1269,36 @@ int info_main(int argc, char *argv[], void *userdata) {
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "-x/--export or -P/--export-prefix cannot be used with --value");
 
+        return 1;
+}
+
+int info_main(int argc, char *argv[], void *userdata) {
+        int r, ret;
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r;
+
+        if (arg_action_type == ACTION_CLEANUP_DB) {
+                cleanup_db();
+                return 0;
+        }
+
+        if (arg_action_type == ACTION_DEVICE_ID_FILE)
+                return stat_device(arg_name, arg_export, arg_export_prefix);
+
         pager_open(arg_pager_flags);
 
-        if (strv_isempty(devices)) {
-                assert(action == ACTION_TREE);
+        if (arg_action_type == ACTION_EXPORT)
+                return export_devices();
+
+        if (strv_isempty(arg_devices)) {
+                assert(arg_action_type == ACTION_TREE);
                 return print_tree(NULL);
         }
 
         ret = 0;
-        STRV_FOREACH(p, devices) {
+        STRV_FOREACH(p, arg_devices) {
                 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
 
                 r = find_device(*p, NULL, &device);
@@ -1204,14 +1328,14 @@ int info_main(int argc, char *argv[], void *userdata) {
                         device = d;
                 }
 
-                if (action == ACTION_QUERY)
-                        r = query_device(query, device);
-                else if (action == ACTION_ATTRIBUTE_WALK) {
+                if (arg_action_type == ACTION_QUERY)
+                        r = query_device(arg_query, device);
+                else if (arg_action_type == ACTION_ATTRIBUTE_WALK) {
                         if (sd_json_format_enabled(arg_json_format_flags))
                                 r = print_device_chain_in_json(device);
                         else
                                 r = print_device_chain(device);
-                } else if (action == ACTION_TREE)
+                } else if (arg_action_type == ACTION_TREE)
                         r = print_tree(device);
                 else
                         assert_not_reached();