]> 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 1894362828fcac12d704b6d345cb1ee5e9482a8a..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,
+        ACTION_ATTRIBUTE_WALK,
+        ACTION_DEVICE_ID_FILE,
+} ActionType;
+
+typedef enum QueryType {
+        QUERY_NAME,
+        QUERY_PATH,
+        QUERY_SYMLINK,
+        QUERY_PROPERTY,
+        QUERY_ALL,
+} 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");
+}
+
+typedef struct SysAttr {
+        const char *name;
+        const char *value;
+} SysAttr;
 
-        for (i = 0; i < ELEMENTSOF(skip); i++)
-                if (streq(name, skip[i]))
-                        return true;
-        return false;
+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"
@@ -77,55 +137,44 @@ 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;
 }
 
-static void print_record(sd_device *device) {
+static int print_record(sd_device *device) {
         const char *str, *val;
         int i;
 
         (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;
 }
 
 static int stat_device(const char *name, bool export, const char *prefix) {
@@ -153,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;
 }
@@ -223,8 +272,69 @@ static void cleanup_db(void) {
                 cleanup_dir(dir5, 0, 1);
 }
 
-static int help(void) {
+static int query_device(QueryType query, sd_device* device) {
+        int r;
+
+        assert(device);
+
+        switch(query) {
+        case QUERY_NAME: {
+                const char *node;
+
+                r = sd_device_get_devname(device, &node);
+                if (r < 0)
+                        return log_error_errno(r, "No device node found: %m");
+
+                if (!arg_root)
+                        assert_se(node = path_startswith(node, "/dev/"));
+                printf("%s\n", node);
+                return 0;
+        }
+
+        case QUERY_SYMLINK: {
+                const char *devlink, *prefix = "";
+
+                FOREACH_DEVICE_DEVLINK(device, devlink) {
+                        if (!arg_root)
+                                assert_se(devlink = path_startswith(devlink, "/dev/"));
+                        printf("%s%s", prefix, devlink);
+                        prefix = " ";
+                }
+                puts("");
+                return 0;
+        }
+
+        case QUERY_PATH: {
+                const char *devpath;
+
+                r = sd_device_get_devpath(device, &devpath);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get device path: %m");
 
+                printf("%s\n", devpath);
+                return 0;
+        }
+
+        case QUERY_PROPERTY: {
+                const char *key, *value;
+
+                FOREACH_DEVICE_PROPERTY(device, key, value)
+                        if (arg_export)
+                                printf("%s%s='%s'\n", strempty(arg_export_prefix), key, value);
+                        else
+                                printf("%s=%s\n", key, value);
+                return 0;
+        }
+
+        case QUERY_ALL:
+                return print_record(device);
+        }
+
+        assert_not_reached("unknown query type");
+        return 0;
+}
+
+static int help(void) {
         printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
                "Query sysfs or the udev database.\n\n"
                "  -h --help                   Print this message\n"
@@ -245,70 +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;
-        bool root = false, export = false;
+        _cleanup_strv_free_ char **devices = NULL;
         _cleanup_free_ char *name = NULL;
-        const char *export_prefix = 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' },
                 {}
         };
 
-        enum action_type {
-                ACTION_QUERY,
-                ACTION_ATTRIBUTE_WALK,
-                ACTION_DEVICE_ID_FILE,
-        } action = ACTION_QUERY;
-
-        enum query_type {
-                QUERY_NAME,
-                QUERY_PATH,
-                QUERY_SYMLINK,
-                QUERY_PROPERTY,
-                QUERY_ALL,
-        } query = QUERY_ALL;
-
-        while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
+        ActionType action = ACTION_QUERY;
+        QueryType query = QUERY_ALL;
+
+        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"))
@@ -321,13 +416,11 @@ 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':
-                        root = true;
+                        arg_root = true;
                         break;
                 case 'd':
                         action = ACTION_DEVICE_ID_FILE;
@@ -344,10 +437,19 @@ int info_main(int argc, char *argv[], void *userdata) {
                         cleanup_db();
                         return 0;
                 case 'x':
-                        export = true;
+                        arg_export = true;
                         break;
                 case 'P':
-                        export_prefix = optarg;
+                        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();
@@ -359,94 +461,54 @@ int info_main(int argc, char *argv[], void *userdata) {
                         assert_not_reached("Unknown option");
                 }
 
-        switch (action) {
-        case ACTION_QUERY:
-                if (!device) {
-                        if (!argv[optind]) {
-                                help();
-                                return -EINVAL;
-                        }
-                        r = find_device(argv[optind], NULL, &device);
-                        if (r < 0)
-                                return log_error_errno(r, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected: %m");
-                }
-
-                switch(query) {
-                case QUERY_NAME: {
-                        const char *node;
+        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);
+        }
 
-                        r = sd_device_get_devname(device, &node);
-                        if (r < 0)
-                                return log_error_errno(r, "no device node found: %m");
+        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");
+
+        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\", 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",  *p);
 
-                        if (root)
-                                printf("%s\n", node);
-                        else
-                                printf("%s\n", node + STRLEN("/dev/"));
-                        break;
-                }
-                case QUERY_SYMLINK: {
-                        const char *devlink;
-                        bool first = true;
-
-                        FOREACH_DEVICE_DEVLINK(device, devlink) {
-                                if (!first)
-                                        printf(" ");
-                                if (root)
-                                        printf("%s", devlink);
-                                else
-                                        printf("%s", devlink + STRLEN("/dev/"));
-
-                                first = false;
-                        }
-                        printf("\n");
-                        break;
-                }
-                case QUERY_PATH: {
-                        const char *devpath;
+                if (arg_wait_for_initialization_timeout > 0) {
+                        sd_device *d;
 
-                        r = sd_device_get_devpath(device, &devpath);
+                        r = device_wait_for_initialization(device, NULL, arg_wait_for_initialization_timeout, &d);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to get device path: %m");
+                                return r;
 
-                        printf("%s\n", devpath);
-                        return 0;
+                        sd_device_unref(device);
+                        device = d;
                 }
-                case QUERY_PROPERTY: {
-                        const char *key, *value;
-
-                        FOREACH_DEVICE_PROPERTY(device, key, value)
-                                if (export)
-                                        printf("%s%s='%s'\n", strempty(export_prefix), key, value);
-                                else
-                                        printf("%s=%s\n", key, value);
 
-                        break;
-                }
-                case QUERY_ALL:
-                        print_record(device);
-                        break;
-                default:
-                        assert_not_reached("unknown query type");
-                }
-                break;
-        case ACTION_ATTRIBUTE_WALK:
-                if (!device && argv[optind]) {
-                        r = find_device(argv[optind], NULL, &device);
-                        if (r < 0)
-                                return log_error_errno(r, "Unknown device, absolute path in /dev/ or /sys expected: %m");
-                }
-                if (!device) {
-                        log_error("Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.");
-                        return -EINVAL;
-                }
-                print_device_chain(device);
-                break;
-        case ACTION_DEVICE_ID_FILE:
-                r = stat_device(name, export, 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;
-                break;
         }
 
         return 0;