From a6b4b2fa010f6dc5e18f1a14d93204c6c1416278 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 12 Oct 2023 11:20:06 +0200 Subject: [PATCH] udev: Enable filtering the output of udevadm info --export-db Let's support the same filtering options that we also support in udevadm trigger in udevadm info to filter the devices produced by --export-db. One difference is that all properties specified by --propery-match= have to be satisfied in udevadm info unlike udevadm trigger where just one of them has to be satisfied. --- man/udevadm.xml | 74 +++++++++++ src/udev/udevadm-info.c | 236 +++++++++++++++++++++++++++++----- test/units/testsuite-17.10.sh | 11 ++ 3 files changed, 291 insertions(+), 30 deletions(-) diff --git a/man/udevadm.xml b/man/udevadm.xml index 3bc62667105..76c54c37846 100644 --- a/man/udevadm.xml +++ b/man/udevadm.xml @@ -229,6 +229,80 @@ + + + + + When used with , only show devices of or not of the given + subsystem respectively. + + + + + + + + + When used with , only show devices matching or not matching the + given attribute respectively. + + + + + + + + When used with , only show devices matching the given property + and value. + + + + + + + + When used with , only show devices with the given tag. + + + + + + + + When used with , only show devices with the given + /sys path. + + + + + + + + When used with , only show devices with the given name in + /dev. + + + + + + + + When used with , only show devices with the given parent + device. + + + + + + + + + When used with , only show devices that are initialized or not + initialized respectively. + + + + diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c index 22e5496c40c..6a483fa6321 100644 --- a/src/udev/udevadm-info.c +++ b/src/udev/udevadm-info.c @@ -318,18 +318,11 @@ static int stat_device(const char *name, bool export, const char *prefix) { return 0; } -static int export_devices(void) { - _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; +static int export_devices(sd_device_enumerator *e) { sd_device *d; int r; - r = sd_device_enumerator_new(&e); - if (r < 0) - return log_oom(); - - r = sd_device_enumerator_allow_uninitialized(e); - if (r < 0) - return log_error_errno(r, "Failed to set allowing uninitialized flag: %m"); + assert(e); r = device_enumerator_scan_devices(e); if (r < 0) @@ -560,7 +553,23 @@ static int help(void) { " -w --wait-for-initialization[=SECONDS]\n" " Wait for device to be initialized\n" " --no-pager Do not pipe output into a pager\n" - " --json=pretty|short|off Generate JSON output\n", + " --json=pretty|short|off Generate JSON output\n" + " --subsystem-match=SUBSYSTEM\n" + " Query devices matching a subsystem\n" + " --subsystem-nomatch=SUBSYSTEM\n" + " Query devices not matching a subsystem\n" + " --attr-match=FILE[=VALUE]\n" + " Query devices that match an attribute\n" + " --attr-nomatch=FILE[=VALUE]\n" + " Query devices that do not match an attribute\n" + " --property-match=KEY=VALUE\n" + " Query devices with matching properties\n" + " --tag-match=TAG Query devices with a matching tag\n" + " --sysname-match=NAME Query devices with this /sys path\n" + " --name-match=NAME Query devices with this /dev name\n" + " --parent-match=NAME Query devices with this parent device\n" + " --initialized-match Query devices that are already initialized\n" + " --initialized-nomatch Query devices that are not initialized yet\n", program_invocation_short_name); return 0; @@ -723,7 +732,49 @@ 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; +} + +static int parse_key_value_argument(const char *s, char **key, char **value) { + _cleanup_free_ char *k = NULL, *v = NULL; + int r; + + assert(s); + assert(key); + assert(value); + + r = extract_many_words(&s, "=", EXTRACT_DONT_COALESCE_SEPARATORS, &k, &v, NULL); + if (r < 0) + return log_error_errno(r, "Failed to parse key/value pair %s: %m", s); + if (r < 2) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing '=' in key/value pair %s.", s); + + if (!filename_is_valid(k)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "%s is not a valid key name", k); + + free_and_replace(*key, k); + free_and_replace(*value, v); + 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; @@ -733,27 +784,49 @@ int info_main(int argc, char *argv[], void *userdata) { ARG_VALUE, ARG_NO_PAGER, ARG_JSON, + ARG_SUBSYSTEM_MATCH, + ARG_SUBSYSTEM_NOMATCH, + ARG_ATTR_MATCH, + ARG_ATTR_NOMATCH, + ARG_PROPERTY_MATCH, + ARG_TAG_MATCH, + ARG_SYSNAME_MATCH, + ARG_NAME_MATCH, + ARG_PARENT_MATCH, + ARG_INITIALIZED_MATCH, + ARG_INITIALIZED_NOMATCH, }; static const struct option options[] = { - { "attribute-walk", no_argument, NULL, 'a' }, - { "tree", no_argument, NULL, 't' }, - { "cleanup-db", no_argument, NULL, 'c' }, - { "device-id-of-file", required_argument, NULL, 'd' }, - { "export", no_argument, NULL, 'x' }, - { "export-db", no_argument, NULL, 'e' }, - { "export-prefix", required_argument, NULL, 'P' }, - { "help", no_argument, NULL, 'h' }, - { "name", required_argument, NULL, 'n' }, - { "path", required_argument, NULL, 'p' }, - { "property", required_argument, NULL, ARG_PROPERTY }, - { "query", required_argument, NULL, 'q' }, - { "root", no_argument, NULL, 'r' }, - { "value", no_argument, NULL, ARG_VALUE }, - { "version", no_argument, NULL, 'V' }, - { "wait-for-initialization", optional_argument, NULL, 'w' }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "json", required_argument, NULL, ARG_JSON }, + { "attribute-walk", no_argument, NULL, 'a' }, + { "tree", no_argument, NULL, 't' }, + { "cleanup-db", no_argument, NULL, 'c' }, + { "device-id-of-file", required_argument, NULL, 'd' }, + { "export", no_argument, NULL, 'x' }, + { "export-db", no_argument, NULL, 'e' }, + { "export-prefix", required_argument, NULL, 'P' }, + { "help", no_argument, NULL, 'h' }, + { "name", required_argument, NULL, 'n' }, + { "path", required_argument, NULL, 'p' }, + { "property", required_argument, NULL, ARG_PROPERTY }, + { "query", required_argument, NULL, 'q' }, + { "root", no_argument, NULL, 'r' }, + { "value", no_argument, NULL, ARG_VALUE }, + { "version", no_argument, NULL, 'V' }, + { "wait-for-initialization", optional_argument, NULL, 'w' }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "json", required_argument, NULL, ARG_JSON }, + { "subsystem-match", required_argument, NULL, ARG_SUBSYSTEM_MATCH }, + { "subsystem-nomatch", required_argument, NULL, ARG_SUBSYSTEM_NOMATCH }, + { "attr-match", required_argument, NULL, ARG_ATTR_MATCH }, + { "attr-nomatch", required_argument, NULL, ARG_ATTR_NOMATCH }, + { "property-match", required_argument, NULL, ARG_PROPERTY_MATCH }, + { "tag-match", required_argument, NULL, ARG_TAG_MATCH }, + { "sysname-match", required_argument, NULL, ARG_SYSNAME_MATCH }, + { "name-match", required_argument, NULL, ARG_NAME_MATCH }, + { "parent-match", required_argument, NULL, ARG_PARENT_MATCH }, + { "initialized-match", no_argument, NULL, ARG_INITIALIZED_MATCH }, + { "initialized-nomatch", no_argument, NULL, ARG_INITIALIZED_NOMATCH }, {} }; @@ -858,6 +931,104 @@ int info_main(int argc, char *argv[], void *userdata) { return r; break; + case ARG_SUBSYSTEM_MATCH: + case ARG_SUBSYSTEM_NOMATCH: + r = ensure_device_enumerator(&e); + if (r < 0) + return r; + + r = sd_device_enumerator_add_match_subsystem(e, optarg, c == ARG_SUBSYSTEM_MATCH); + if (r < 0) + return log_error_errno(r, "Failed to add%s subsystem match '%s': %m", + c == ARG_SUBSYSTEM_MATCH ? "" : " negative", optarg); + + 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, &k, &v); + if (r < 0) + return r; + + r = sd_device_enumerator_add_match_sysattr(e, k, v, c == ARG_ATTR_MATCH); + if (r < 0) + return log_error_errno(r, "Failed to add%s sysattr match '%s=%s': %m", + c == ARG_ATTR_MATCH ? "" : " negative", k, v); + break; + } + + case ARG_PROPERTY_MATCH: { + _cleanup_free_ char *k = NULL, *v = NULL; + + r = ensure_device_enumerator(&e); + if (r < 0) + return r; + + r = parse_key_value_argument(optarg, &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); + break; + } + + case ARG_TAG_MATCH: + r = ensure_device_enumerator(&e); + 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); + break; + + case ARG_SYSNAME_MATCH: + r = ensure_device_enumerator(&e); + if (r < 0) + return r; + + r = sd_device_enumerator_add_match_sysname(e, optarg); + if (r < 0) + return log_error_errno(r, "Failed to add sysname match '%s': %m", optarg); + 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); + 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; + + r = device_enumerator_add_match_parent_incremental(e, dev); + if (r < 0) + return log_error_errno(r, "Failed to add parent match '%s': %m", optarg); + break; + } + + case ARG_INITIALIZED_MATCH: + case ARG_INITIALIZED_NOMATCH: + r = ensure_device_enumerator(&e); + if (r < 0) + return r; + + 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"); + break; + case '?': return -EINVAL; default: @@ -872,8 +1043,13 @@ int info_main(int argc, char *argv[], void *userdata) { return stat_device(name, arg_export, arg_export_prefix); } - if (action == ACTION_EXPORT) - return export_devices(); + 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); if (r < 0) diff --git a/test/units/testsuite-17.10.sh b/test/units/testsuite-17.10.sh index 5b319bca913..b4724653f35 100755 --- a/test/units/testsuite-17.10.sh +++ b/test/units/testsuite-17.10.sh @@ -80,6 +80,17 @@ udevadm info -e >/dev/null udevadm info -e --json=off >/dev/null udevadm info -e --json=pretty | jq . >/dev/null udevadm info -e --json=short | jq . >/dev/null +udevadm info -e --subsystem-match acpi >/dev/null +udevadm info -e --subsystem-nomatch acpi >/dev/null +udevadm info -e --attr-match ifindex=2 >/dev/null +udevadm info -e --attr-nomatch ifindex=2 >/dev/null +udevadm info -e --property-match SUBSYSTEM=acpi >/dev/null +udevadm info -e --tag-match systemd >/dev/null +udevadm info -e --sysname-match lo >/dev/null +udevadm info -e --name-match /sys/class/net/$netdev >/dev/null +udevadm info -e --parent-match /sys/class/net/$netdev >/dev/null +udevadm info -e --initialized-match >/dev/null +udevadm info -e --initialized-nomatch >/dev/null # udevadm info -c udevadm info -w /sys/class/net/$netdev udevadm info --wait-for-initialization=5 /sys/class/net/$netdev -- 2.47.3