ACTION_DEVICE_ID_FILE,
ACTION_TREE,
ACTION_EXPORT,
+ ACTION_CLEANUP_DB,
} ActionType;
typedef enum QueryType {
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
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");
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,
{}
};
- 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. */
return log_oom();
}
break;
+
case ARG_VALUE:
arg_value = true;
break;
+
case 'n':
case 'p': {
const char *prefix = c == 'n' ? "/dev/" : "/sys/";
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);
} 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;
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");
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);
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();