]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/udev/udevadm-info.c
udev: fix codesonar warnings
[thirdparty/systemd.git] / src / udev / udevadm-info.c
index 4f7a4a388cc9348d5e5516921b79873d4af345f0..ae6d8caf54e02c39c4b5f4245b5523cc9713eb97 100644 (file)
@@ -6,7 +6,6 @@
 #include <getopt.h>
 #include <stddef.h>
 #include <stdio.h>
-#include <string.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
 #include "device-util.h"
 #include "dirent-util.h"
 #include "fd-util.h"
+#include "sort-util.h"
+#include "string-table.h"
 #include "string-util.h"
-#include "udevadm.h"
+#include "udev-util.h"
 #include "udevadm-util.h"
+#include "udevadm.h"
 
 typedef enum ActionType {
         ACTION_QUERY,
@@ -39,53 +41,93 @@ typedef enum QueryType {
 static bool arg_root = false;
 static bool arg_export = false;
 static const char *arg_export_prefix = NULL;
+static usec_t arg_wait_for_initialization_timeout = 0;
 
 static bool skip_attribute(const char *name) {
-        static const char* const skip[] = {
-                "uevent",
-                "dev",
-                "modalias",
-                "resource",
-                "driver",
-                "subsystem",
-                "module",
-        };
-        unsigned i;
+        /* Those are either displayed separately or should not be shown at all. */
+        return STR_IN_SET(name,
+                          "uevent",
+                          "dev",
+                          "modalias",
+                          "resource",
+                          "driver",
+                          "subsystem",
+                          "module");
+}
 
-        for (i = 0; i < ELEMENTSOF(skip); i++)
-                if (streq(name, skip[i]))
-                        return true;
-        return false;
+typedef struct SysAttr {
+        const char *name;
+        const char *value;
+} SysAttr;
+
+static int sysattr_compare(const SysAttr *a, const SysAttr *b) {
+        return strcmp(a->name, b->name);
 }
 
-static void print_all_attributes(sd_device *device, const char *key) {
+static int print_all_attributes(sd_device *device, bool is_parent) {
+        _cleanup_free_ SysAttr *sysattrs = NULL;
+        size_t n_items = 0, n_allocated = 0;
         const char *name, *value;
 
-        FOREACH_DEVICE_PROPERTY(device, name, value) {
+        value = NULL;
+        (void) sd_device_get_devpath(device, &value);
+        printf("  looking at %sdevice '%s':\n", is_parent ? "parent " : "", strempty(value));
+
+        value = NULL;
+        (void) sd_device_get_sysname(device, &value);
+        printf("    %s==\"%s\"\n", is_parent ? "KERNELS" : "KERNEL", strempty(value));
+
+        value = NULL;
+        (void) sd_device_get_subsystem(device, &value);
+        printf("    %s==\"%s\"\n", is_parent ? "SUBSYSTEMS" : "SUBSYSTEM", strempty(value));
+
+        value = NULL;
+        (void) sd_device_get_driver(device, &value);
+        printf("    %s==\"%s\"\n", is_parent ? "DRIVERS" : "DRIVER", strempty(value));
+
+        FOREACH_DEVICE_SYSATTR(device, name) {
                 size_t len;
 
                 if (skip_attribute(name))
                         continue;
 
+                if (sd_device_get_sysattr_value(device, name, &value) < 0)
+                        continue;
+
                 /* skip any values that look like a path */
                 if (value[0] == '/')
                         continue;
 
                 /* skip nonprintable attributes */
                 len = strlen(value);
-                while (len > 0 && isprint(value[len-1]))
+                while (len > 0 && isprint((unsigned char) value[len-1]))
                         len--;
                 if (len > 0)
                         continue;
 
-                printf("    %s{%s}==\"%s\"\n", key, name, value);
+                if (!GREEDY_REALLOC(sysattrs, n_allocated, n_items + 1))
+                        return log_oom();
+
+                sysattrs[n_items] = (SysAttr) {
+                        .name = name,
+                        .value = value,
+                };
+                n_items++;
         }
-        printf("\n");
+
+        typesafe_qsort(sysattrs, n_items, sysattr_compare);
+
+        for (size_t i = 0; i < n_items; i++)
+                printf("    %s{%s}==\"%s\"\n", is_parent ? "ATTRS" : "ATTR", sysattrs[i].name, sysattrs[i].value);
+
+        puts("");
+
+        return 0;
 }
 
 static int print_device_chain(sd_device *device) {
         sd_device *child, *parent;
-        const char *str;
+        int r;
 
         printf("\n"
                "Udevadm info starts with the device specified by the devpath and then\n"
@@ -95,30 +137,14 @@ static int print_device_chain(sd_device *device) {
                "and the attributes from one single parent device.\n"
                "\n");
 
-        (void) sd_device_get_devpath(device, &str);
-        printf("  looking at device '%s':\n", str);
-        (void) sd_device_get_sysname(device, &str);
-        printf("    KERNEL==\"%s\"\n", str);
-        if (sd_device_get_subsystem(device, &str) < 0)
-                str = "";
-        printf("    SUBSYSTEM==\"%s\"\n", str);
-        if (sd_device_get_driver(device, &str) < 0)
-                str = "";
-        printf("    DRIVER==\"%s\"\n", str);
-        print_all_attributes(device, "ATTR");
+        r = print_all_attributes(device, false);
+        if (r < 0)
+                return r;
 
         for (child = device; sd_device_get_parent(child, &parent) >= 0; child = parent) {
-                (void) sd_device_get_devpath(parent, &str);
-                printf("  looking at parent device '%s':\n", str);
-                (void) sd_device_get_sysname(parent, &str);
-                printf("    KERNELS==\"%s\"\n", str);
-                if (sd_device_get_subsystem(parent, &str) < 0)
-                        str = "";
-                printf("    SUBSYSTEMS==\"%s\"\n", str);
-                if (sd_device_get_driver(parent, &str) < 0)
-                        str = "";
-                printf("    DRIVERS==\"%s\"\n", str);
-                print_all_attributes(parent, "ATTRS");
+                r = print_all_attributes(parent, true);
+                if (r < 0)
+                        return r;
         }
 
         return 0;
@@ -131,19 +157,23 @@ static int print_record(sd_device *device) {
         (void) sd_device_get_devpath(device, &str);
         printf("P: %s\n", str);
 
-        if (sd_device_get_devname(device, &str) >= 0)
-                printf("N: %s\n", str + STRLEN("/dev/"));
+        if (sd_device_get_devname(device, &str) >= 0) {
+                assert_se(val = path_startswith(str, "/dev/"));
+                printf("N: %s\n", val);
+        }
 
         if (device_get_devlink_priority(device, &i) >= 0)
                 printf("L: %i\n", i);
 
-        FOREACH_DEVICE_DEVLINK(device, str)
-                printf("S: %s\n", str + STRLEN("/dev/"));
+        FOREACH_DEVICE_DEVLINK(device, str) {
+                assert_se(val = path_startswith(str, "/dev/"));
+                printf("S: %s\n", val);
+        }
 
         FOREACH_DEVICE_PROPERTY(device, str, val)
                 printf("E: %s=%s\n", str, val);
 
-        printf("\n");
+        puts("");
         return 0;
 }
 
@@ -172,18 +202,18 @@ static int export_devices(void) {
 
         r = sd_device_enumerator_new(&e);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = sd_device_enumerator_allow_uninitialized(e);
         if (r < 0)
-                return r;
+                return log_error_errno(r, "Failed to set allowing uninitialized flag: %m");
 
         r = device_enumerator_scan_devices(e);
         if (r < 0)
-                return r;
+                return log_error_errno(r, "Failed to scan devices: %m");
 
         FOREACH_DEVICE_AND_SUBSYSTEM(e, d)
-                print_record(d);
+                (void) print_record(d);
 
         return 0;
 }
@@ -255,28 +285,22 @@ static int query_device(QueryType query, sd_device* device) {
                 if (r < 0)
                         return log_error_errno(r, "No device node found: %m");
 
-                if (arg_root)
-                        printf("%s\n", node);
-                else
-                        printf("%s\n", node + STRLEN("/dev/"));
+                if (!arg_root)
+                        assert_se(node = path_startswith(node, "/dev/"));
+                printf("%s\n", node);
                 return 0;
         }
 
         case QUERY_SYMLINK: {
-                const char *devlink;
-                bool first = true;
+                const char *devlink, *prefix = "";
 
                 FOREACH_DEVICE_DEVLINK(device, devlink) {
-                        if (!first)
-                                printf(" ");
-                        if (arg_root)
-                                printf("%s", devlink);
-                        else
-                                printf("%s", devlink + STRLEN("/dev/"));
-
-                        first = false;
+                        if (!arg_root)
+                                assert_se(devlink = path_startswith(devlink, "/dev/"));
+                        printf("%s%s", prefix, devlink);
+                        prefix = " ";
                 }
-                printf("\n");
+                puts("");
                 return 0;
         }
 
@@ -331,57 +355,55 @@ static int help(void) {
                "  -P --export-prefix          Export the key name with a prefix\n"
                "  -e --export-db              Export the content of the udev database\n"
                "  -c --cleanup-db             Clean up the udev database\n"
+               "  -w --wait-for-initialization[=SECONDS]\n"
+               "                              Wait for device to be initialized\n"
                , program_invocation_short_name);
 
         return 0;
 }
 
 int info_main(int argc, char *argv[], void *userdata) {
-        _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+        _cleanup_strv_free_ char **devices = NULL;
         _cleanup_free_ char *name = NULL;
         int c, r;
 
         static const struct option options[] = {
-                { "name",              required_argument, NULL, 'n' },
-                { "path",              required_argument, NULL, 'p' },
-                { "query",             required_argument, NULL, 'q' },
-                { "attribute-walk",    no_argument,       NULL, 'a' },
-                { "cleanup-db",        no_argument,       NULL, 'c' },
-                { "export-db",         no_argument,       NULL, 'e' },
-                { "root",              no_argument,       NULL, 'r' },
-                { "device-id-of-file", required_argument, NULL, 'd' },
-                { "export",            no_argument,       NULL, 'x' },
-                { "export-prefix",     required_argument, NULL, 'P' },
-                { "version",           no_argument,       NULL, 'V' },
-                { "help",              no_argument,       NULL, 'h' },
+                { "name",                    required_argument, NULL, 'n' },
+                { "path",                    required_argument, NULL, 'p' },
+                { "query",                   required_argument, NULL, 'q' },
+                { "attribute-walk",          no_argument,       NULL, 'a' },
+                { "cleanup-db",              no_argument,       NULL, 'c' },
+                { "export-db",               no_argument,       NULL, 'e' },
+                { "root",                    no_argument,       NULL, 'r' },
+                { "device-id-of-file",       required_argument, NULL, 'd' },
+                { "export",                  no_argument,       NULL, 'x' },
+                { "export-prefix",           required_argument, NULL, 'P' },
+                { "wait-for-initialization", optional_argument, NULL, 'w' },
+                { "version",                 no_argument,       NULL, 'V' },
+                { "help",                    no_argument,       NULL, 'h' },
                 {}
         };
 
         ActionType action = ACTION_QUERY;
         QueryType query = QUERY_ALL;
 
-        while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:w::Vh", options, NULL)) >= 0)
                 switch (c) {
                 case 'n':
-                        if (device) {
-                                log_error("device already specified");
-                                return -EINVAL;
-                        }
+                case 'p': {
+                        const char *prefix = c == 'n' ? "/dev/" : "/sys/";
+                        char *path;
 
-                        r = find_device(optarg, "/dev/", &device);
-                        if (r < 0)
-                                return log_error_errno(r, "device node not found: %m");
-                        break;
-                case 'p':
-                        if (device) {
-                                log_error("device already specified");
-                                return -EINVAL;
-                        }
+                        path = path_join(path_startswith(optarg, prefix) ? NULL : prefix, optarg);
+                        if (!path)
+                                return log_oom();
 
-                        r = find_device(optarg, "/sys", &device);
+                        r = strv_consume(&devices, path);
                         if (r < 0)
-                                return log_error_errno(r, "syspath not found: %m");
+                                return log_oom();
                         break;
+                }
+
                 case 'q':
                         action = ACTION_QUERY;
                         if (streq(optarg, "property") || streq(optarg, "env"))
@@ -394,10 +416,8 @@ int info_main(int argc, char *argv[], void *userdata) {
                                 query = QUERY_PATH;
                         else if (streq(optarg, "all"))
                                 query = QUERY_ALL;
-                        else {
-                                log_error("unknown query type");
-                                return -EINVAL;
-                        }
+                        else
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "unknown query type");
                         break;
                 case 'r':
                         arg_root = true;
@@ -420,8 +440,17 @@ int info_main(int argc, char *argv[], void *userdata) {
                         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);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse timeout value: %m");
+                        } else
+                                arg_wait_for_initialization_timeout = USEC_INFINITY;
+                        break;
                 case 'V':
                         return print_version();
                 case 'h':
@@ -432,35 +461,55 @@ int info_main(int argc, char *argv[], void *userdata) {
                         assert_not_reached("Unknown option");
                 }
 
-        if (IN_SET(action, ACTION_QUERY, ACTION_ATTRIBUTE_WALK) &&
-            !device) {
-                /* A device argument is required. It may be an option or a positional arg. */
-
-                if (!argv[optind])
+        if (action == ACTION_DEVICE_ID_FILE) {
+                if (argv[optind])
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                               "A device name or path is required");
+                                               "Positional arguments are not allowed with -d/--device-id-of-file.");
+                assert(name);
+                return stat_device(name, arg_export, arg_export_prefix);
+        }
+
+        r = strv_extend_strv(&devices, argv + optind, false);
+        if (r < 0)
+                return log_error_errno(r, "Failed to build argument list: %m");
+
+        if (strv_isempty(devices))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "A device name or path is required");
+        if (action == ACTION_ATTRIBUTE_WALK && strv_length(devices) > 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Only one device may be specified with -a/--attribute-walk");
 
-                r = find_device(argv[optind], NULL, &device);
+        char **p;
+        STRV_FOREACH(p, devices) {
+                _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+
+                r = find_device(*p, NULL, &device);
                 if (r == -EINVAL)
-                        return log_error_errno(r, "Bad argument \"%s\", an absolute path in /dev/ or /sys expected: %m",
-                                               argv[optind]);
+                        return log_error_errno(r, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys or a unit name: %m", *p);
                 if (r < 0)
-                        return log_error_errno(r, "Unknown device \"%s\": %m",  argv[optind]);
-        }
+                        return log_error_errno(r, "Unknown device \"%s\": %m",  *p);
+
+                if (arg_wait_for_initialization_timeout > 0) {
+                        sd_device *d;
 
-        switch (action) {
-        case ACTION_QUERY:
-                assert(device);
-                return query_device(query, device);
+                        r = device_wait_for_initialization(device, NULL, arg_wait_for_initialization_timeout, &d);
+                        if (r < 0)
+                                return r;
 
-        case ACTION_ATTRIBUTE_WALK:
-                assert(device);
-                return print_device_chain(device);
+                        sd_device_unref(device);
+                        device = d;
+                }
 
-        case ACTION_DEVICE_ID_FILE:
-                assert(name);
-                return stat_device(name, arg_export, arg_export_prefix);
+                if (action == ACTION_QUERY)
+                        r = query_device(query, device);
+                else if (action == ACTION_ATTRIBUTE_WALK)
+                        r = print_device_chain(device);
+                else
+                        assert_not_reached("Unknown action");
+                if (r < 0)
+                        return r;
         }
 
-        assert_not_reached("Unknown action");
+        return 0;
 }