LCOV - code coverage report
Current view: top level - udev - udevadm-info.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 0 258 0.0 %
Date: 2019-08-22 15:41:25 Functions: 0 11 0.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: GPL-2.0+ */
       2             : 
       3             : #include <ctype.h>
       4             : #include <errno.h>
       5             : #include <fcntl.h>
       6             : #include <getopt.h>
       7             : #include <stddef.h>
       8             : #include <stdio.h>
       9             : #include <string.h>
      10             : #include <sys/stat.h>
      11             : #include <unistd.h>
      12             : 
      13             : #include "sd-device.h"
      14             : 
      15             : #include "alloc-util.h"
      16             : #include "device-enumerator-private.h"
      17             : #include "device-private.h"
      18             : #include "device-util.h"
      19             : #include "dirent-util.h"
      20             : #include "fd-util.h"
      21             : #include "string-table.h"
      22             : #include "string-util.h"
      23             : #include "udev-util.h"
      24             : #include "udevadm-util.h"
      25             : #include "udevadm.h"
      26             : 
      27             : typedef enum ActionType {
      28             :         ACTION_QUERY,
      29             :         ACTION_ATTRIBUTE_WALK,
      30             :         ACTION_DEVICE_ID_FILE,
      31             : } ActionType;
      32             : 
      33             : typedef enum QueryType {
      34             :         QUERY_NAME,
      35             :         QUERY_PATH,
      36             :         QUERY_SYMLINK,
      37             :         QUERY_PROPERTY,
      38             :         QUERY_ALL,
      39             : } QueryType;
      40             : 
      41             : static bool arg_root = false;
      42             : static bool arg_export = false;
      43             : static const char *arg_export_prefix = NULL;
      44             : static usec_t arg_wait_for_initialization_timeout = 0;
      45             : 
      46           0 : static bool skip_attribute(const char *name) {
      47             :         static const char* const skip[] = {
      48             :                 "uevent",
      49             :                 "dev",
      50             :                 "modalias",
      51             :                 "resource",
      52             :                 "driver",
      53             :                 "subsystem",
      54             :                 "module",
      55             :         };
      56             : 
      57           0 :         return string_table_lookup(skip, ELEMENTSOF(skip), name) >= 0;
      58             : }
      59             : 
      60           0 : static void print_all_attributes(sd_device *device, const char *key) {
      61             :         const char *name, *value;
      62             : 
      63           0 :         FOREACH_DEVICE_SYSATTR(device, name) {
      64             :                 size_t len;
      65             : 
      66           0 :                 if (skip_attribute(name))
      67           0 :                         continue;
      68             : 
      69           0 :                 if (sd_device_get_sysattr_value(device, name, &value) < 0)
      70           0 :                         continue;
      71             : 
      72             :                 /* skip any values that look like a path */
      73           0 :                 if (value[0] == '/')
      74           0 :                         continue;
      75             : 
      76             :                 /* skip nonprintable attributes */
      77           0 :                 len = strlen(value);
      78           0 :                 while (len > 0 && isprint(value[len-1]))
      79           0 :                         len--;
      80           0 :                 if (len > 0)
      81           0 :                         continue;
      82             : 
      83           0 :                 printf("    %s{%s}==\"%s\"\n", key, name, value);
      84             :         }
      85           0 :         puts("");
      86           0 : }
      87             : 
      88           0 : static int print_device_chain(sd_device *device) {
      89             :         sd_device *child, *parent;
      90             :         const char *str;
      91             : 
      92           0 :         printf("\n"
      93             :                "Udevadm info starts with the device specified by the devpath and then\n"
      94             :                "walks up the chain of parent devices. It prints for every device\n"
      95             :                "found, all possible attributes in the udev rules key format.\n"
      96             :                "A rule to match, can be composed by the attributes of the device\n"
      97             :                "and the attributes from one single parent device.\n"
      98             :                "\n");
      99             : 
     100           0 :         (void) sd_device_get_devpath(device, &str);
     101           0 :         printf("  looking at device '%s':\n", str);
     102           0 :         (void) sd_device_get_sysname(device, &str);
     103           0 :         printf("    KERNEL==\"%s\"\n", str);
     104           0 :         if (sd_device_get_subsystem(device, &str) < 0)
     105           0 :                 str = "";
     106           0 :         printf("    SUBSYSTEM==\"%s\"\n", str);
     107           0 :         if (sd_device_get_driver(device, &str) < 0)
     108           0 :                 str = "";
     109           0 :         printf("    DRIVER==\"%s\"\n", str);
     110           0 :         print_all_attributes(device, "ATTR");
     111             : 
     112           0 :         for (child = device; sd_device_get_parent(child, &parent) >= 0; child = parent) {
     113           0 :                 (void) sd_device_get_devpath(parent, &str);
     114           0 :                 printf("  looking at parent device '%s':\n", str);
     115           0 :                 (void) sd_device_get_sysname(parent, &str);
     116           0 :                 printf("    KERNELS==\"%s\"\n", str);
     117           0 :                 if (sd_device_get_subsystem(parent, &str) < 0)
     118           0 :                         str = "";
     119           0 :                 printf("    SUBSYSTEMS==\"%s\"\n", str);
     120           0 :                 if (sd_device_get_driver(parent, &str) < 0)
     121           0 :                         str = "";
     122           0 :                 printf("    DRIVERS==\"%s\"\n", str);
     123           0 :                 print_all_attributes(parent, "ATTRS");
     124             :         }
     125             : 
     126           0 :         return 0;
     127             : }
     128             : 
     129           0 : static int print_record(sd_device *device) {
     130             :         const char *str, *val;
     131             :         int i;
     132             : 
     133           0 :         (void) sd_device_get_devpath(device, &str);
     134           0 :         printf("P: %s\n", str);
     135             : 
     136           0 :         if (sd_device_get_devname(device, &str) >= 0) {
     137           0 :                 assert_se(val = path_startswith(str, "/dev/"));
     138           0 :                 printf("N: %s\n", val);
     139             :         }
     140             : 
     141           0 :         if (device_get_devlink_priority(device, &i) >= 0)
     142           0 :                 printf("L: %i\n", i);
     143             : 
     144           0 :         FOREACH_DEVICE_DEVLINK(device, str) {
     145           0 :                 assert_se(val = path_startswith(str, "/dev/"));
     146           0 :                 printf("S: %s\n", val);
     147             :         }
     148             : 
     149           0 :         FOREACH_DEVICE_PROPERTY(device, str, val)
     150           0 :                 printf("E: %s=%s\n", str, val);
     151             : 
     152           0 :         puts("");
     153           0 :         return 0;
     154             : }
     155             : 
     156           0 : static int stat_device(const char *name, bool export, const char *prefix) {
     157             :         struct stat statbuf;
     158             : 
     159           0 :         if (stat(name, &statbuf) != 0)
     160           0 :                 return -errno;
     161             : 
     162           0 :         if (export) {
     163           0 :                 if (!prefix)
     164           0 :                         prefix = "INFO_";
     165           0 :                 printf("%sMAJOR=%u\n"
     166             :                        "%sMINOR=%u\n",
     167             :                        prefix, major(statbuf.st_dev),
     168             :                        prefix, minor(statbuf.st_dev));
     169             :         } else
     170           0 :                 printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev));
     171           0 :         return 0;
     172             : }
     173             : 
     174           0 : static int export_devices(void) {
     175           0 :         _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
     176             :         sd_device *d;
     177             :         int r;
     178             : 
     179           0 :         r = sd_device_enumerator_new(&e);
     180           0 :         if (r < 0)
     181           0 :                 return r;
     182             : 
     183           0 :         r = sd_device_enumerator_allow_uninitialized(e);
     184           0 :         if (r < 0)
     185           0 :                 return r;
     186             : 
     187           0 :         r = device_enumerator_scan_devices(e);
     188           0 :         if (r < 0)
     189           0 :                 return r;
     190             : 
     191           0 :         FOREACH_DEVICE_AND_SUBSYSTEM(e, d)
     192           0 :                 print_record(d);
     193             : 
     194           0 :         return 0;
     195             : }
     196             : 
     197           0 : static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
     198             :         struct dirent *dent;
     199             : 
     200           0 :         if (depth <= 0)
     201           0 :                 return;
     202             : 
     203           0 :         FOREACH_DIRENT_ALL(dent, dir, break) {
     204             :                 struct stat stats;
     205             : 
     206           0 :                 if (dent->d_name[0] == '.')
     207           0 :                         continue;
     208           0 :                 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
     209           0 :                         continue;
     210           0 :                 if ((stats.st_mode & mask) != 0)
     211           0 :                         continue;
     212           0 :                 if (S_ISDIR(stats.st_mode)) {
     213           0 :                         _cleanup_closedir_ DIR *dir2 = NULL;
     214             : 
     215           0 :                         dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
     216           0 :                         if (dir2)
     217           0 :                                 cleanup_dir(dir2, mask, depth-1);
     218             : 
     219           0 :                         (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
     220             :                 } else
     221           0 :                         (void) unlinkat(dirfd(dir), dent->d_name, 0);
     222             :         }
     223             : }
     224             : 
     225           0 : static void cleanup_db(void) {
     226           0 :         _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL, *dir5 = NULL;
     227             : 
     228           0 :         (void) unlink("/run/udev/queue.bin");
     229             : 
     230           0 :         dir1 = opendir("/run/udev/data");
     231           0 :         if (dir1)
     232           0 :                 cleanup_dir(dir1, S_ISVTX, 1);
     233             : 
     234           0 :         dir2 = opendir("/run/udev/links");
     235           0 :         if (dir2)
     236           0 :                 cleanup_dir(dir2, 0, 2);
     237             : 
     238           0 :         dir3 = opendir("/run/udev/tags");
     239           0 :         if (dir3)
     240           0 :                 cleanup_dir(dir3, 0, 2);
     241             : 
     242           0 :         dir4 = opendir("/run/udev/static_node-tags");
     243           0 :         if (dir4)
     244           0 :                 cleanup_dir(dir4, 0, 2);
     245             : 
     246           0 :         dir5 = opendir("/run/udev/watch");
     247           0 :         if (dir5)
     248           0 :                 cleanup_dir(dir5, 0, 1);
     249           0 : }
     250             : 
     251           0 : static int query_device(QueryType query, sd_device* device) {
     252             :         int r;
     253             : 
     254           0 :         assert(device);
     255             : 
     256           0 :         switch(query) {
     257           0 :         case QUERY_NAME: {
     258             :                 const char *node;
     259             : 
     260           0 :                 r = sd_device_get_devname(device, &node);
     261           0 :                 if (r < 0)
     262           0 :                         return log_error_errno(r, "No device node found: %m");
     263             : 
     264           0 :                 if (!arg_root)
     265           0 :                         assert_se(node = path_startswith(node, "/dev/"));
     266           0 :                 printf("%s\n", node);
     267           0 :                 return 0;
     268             :         }
     269             : 
     270           0 :         case QUERY_SYMLINK: {
     271           0 :                 const char *devlink, *prefix = "";
     272             : 
     273           0 :                 FOREACH_DEVICE_DEVLINK(device, devlink) {
     274           0 :                         if (!arg_root)
     275           0 :                                 assert_se(devlink = path_startswith(devlink, "/dev/"));
     276           0 :                         printf("%s%s", prefix, devlink);
     277           0 :                         prefix = " ";
     278             :                 }
     279           0 :                 puts("");
     280           0 :                 return 0;
     281             :         }
     282             : 
     283           0 :         case QUERY_PATH: {
     284             :                 const char *devpath;
     285             : 
     286           0 :                 r = sd_device_get_devpath(device, &devpath);
     287           0 :                 if (r < 0)
     288           0 :                         return log_error_errno(r, "Failed to get device path: %m");
     289             : 
     290           0 :                 printf("%s\n", devpath);
     291           0 :                 return 0;
     292             :         }
     293             : 
     294           0 :         case QUERY_PROPERTY: {
     295             :                 const char *key, *value;
     296             : 
     297           0 :                 FOREACH_DEVICE_PROPERTY(device, key, value)
     298           0 :                         if (arg_export)
     299           0 :                                 printf("%s%s='%s'\n", strempty(arg_export_prefix), key, value);
     300             :                         else
     301           0 :                                 printf("%s=%s\n", key, value);
     302           0 :                 return 0;
     303             :         }
     304             : 
     305           0 :         case QUERY_ALL:
     306           0 :                 return print_record(device);
     307             :         }
     308             : 
     309           0 :         assert_not_reached("unknown query type");
     310             :         return 0;
     311             : }
     312             : 
     313           0 : static int help(void) {
     314           0 :         printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
     315             :                "Query sysfs or the udev database.\n\n"
     316             :                "  -h --help                   Print this message\n"
     317             :                "  -V --version                Print version of the program\n"
     318             :                "  -q --query=TYPE             Query device information:\n"
     319             :                "       name                     Name of device node\n"
     320             :                "       symlink                  Pointing to node\n"
     321             :                "       path                     sysfs device path\n"
     322             :                "       property                 The device properties\n"
     323             :                "       all                      All values\n"
     324             :                "  -p --path=SYSPATH           sysfs device path used for query or attribute walk\n"
     325             :                "  -n --name=NAME              Node or symlink name used for query or attribute walk\n"
     326             :                "  -r --root                   Prepend dev directory to path names\n"
     327             :                "  -a --attribute-walk         Print all key matches walking along the chain\n"
     328             :                "                              of parent devices\n"
     329             :                "  -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
     330             :                "  -x --export                 Export key/value pairs\n"
     331             :                "  -P --export-prefix          Export the key name with a prefix\n"
     332             :                "  -e --export-db              Export the content of the udev database\n"
     333             :                "  -c --cleanup-db             Clean up the udev database\n"
     334             :                "  -w --wait-for-initialization[=SECONDS]\n"
     335             :                "                              Wait for device to be initialized\n"
     336             :                , program_invocation_short_name);
     337             : 
     338           0 :         return 0;
     339             : }
     340             : 
     341           0 : int info_main(int argc, char *argv[], void *userdata) {
     342           0 :         _cleanup_strv_free_ char **devices = NULL;
     343           0 :         _cleanup_free_ char *name = NULL;
     344             :         int c, r;
     345             : 
     346             :         static const struct option options[] = {
     347             :                 { "name",                    required_argument, NULL, 'n' },
     348             :                 { "path",                    required_argument, NULL, 'p' },
     349             :                 { "query",                   required_argument, NULL, 'q' },
     350             :                 { "attribute-walk",          no_argument,       NULL, 'a' },
     351             :                 { "cleanup-db",              no_argument,       NULL, 'c' },
     352             :                 { "export-db",               no_argument,       NULL, 'e' },
     353             :                 { "root",                    no_argument,       NULL, 'r' },
     354             :                 { "device-id-of-file",       required_argument, NULL, 'd' },
     355             :                 { "export",                  no_argument,       NULL, 'x' },
     356             :                 { "export-prefix",           required_argument, NULL, 'P' },
     357             :                 { "wait-for-initialization", optional_argument, NULL, 'w' },
     358             :                 { "version",                 no_argument,       NULL, 'V' },
     359             :                 { "help",                    no_argument,       NULL, 'h' },
     360             :                 {}
     361             :         };
     362             : 
     363           0 :         ActionType action = ACTION_QUERY;
     364           0 :         QueryType query = QUERY_ALL;
     365             : 
     366           0 :         while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:w::Vh", options, NULL)) >= 0)
     367           0 :                 switch (c) {
     368           0 :                 case 'n':
     369             :                 case 'p': {
     370           0 :                         const char *prefix = c == 'n' ? "/dev/" : "/sys/";
     371             :                         char *path;
     372             : 
     373           0 :                         path = path_join(path_startswith(optarg, prefix) ? NULL : prefix, optarg);
     374           0 :                         if (!path)
     375           0 :                                 return log_oom();
     376             : 
     377           0 :                         r = strv_consume(&devices, path);
     378           0 :                         if (r < 0)
     379           0 :                                 return log_oom();
     380           0 :                         break;
     381             :                 }
     382             : 
     383           0 :                 case 'q':
     384           0 :                         action = ACTION_QUERY;
     385           0 :                         if (streq(optarg, "property") || streq(optarg, "env"))
     386           0 :                                 query = QUERY_PROPERTY;
     387           0 :                         else if (streq(optarg, "name"))
     388           0 :                                 query = QUERY_NAME;
     389           0 :                         else if (streq(optarg, "symlink"))
     390           0 :                                 query = QUERY_SYMLINK;
     391           0 :                         else if (streq(optarg, "path"))
     392           0 :                                 query = QUERY_PATH;
     393           0 :                         else if (streq(optarg, "all"))
     394           0 :                                 query = QUERY_ALL;
     395             :                         else
     396           0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "unknown query type");
     397           0 :                         break;
     398           0 :                 case 'r':
     399           0 :                         arg_root = true;
     400           0 :                         break;
     401           0 :                 case 'd':
     402           0 :                         action = ACTION_DEVICE_ID_FILE;
     403           0 :                         r = free_and_strdup(&name, optarg);
     404           0 :                         if (r < 0)
     405           0 :                                 return log_oom();
     406           0 :                         break;
     407           0 :                 case 'a':
     408           0 :                         action = ACTION_ATTRIBUTE_WALK;
     409           0 :                         break;
     410           0 :                 case 'e':
     411           0 :                         return export_devices();
     412           0 :                 case 'c':
     413           0 :                         cleanup_db();
     414           0 :                         return 0;
     415           0 :                 case 'x':
     416           0 :                         arg_export = true;
     417           0 :                         break;
     418           0 :                 case 'P':
     419           0 :                         arg_export = true;
     420           0 :                         arg_export_prefix = optarg;
     421           0 :                         break;
     422           0 :                 case 'w':
     423           0 :                         if (optarg) {
     424           0 :                                 r = parse_sec(optarg, &arg_wait_for_initialization_timeout);
     425           0 :                                 if (r < 0)
     426           0 :                                         return log_error_errno(r, "Failed to parse timeout value: %m");
     427             :                         } else
     428           0 :                                 arg_wait_for_initialization_timeout = USEC_INFINITY;
     429           0 :                         break;
     430           0 :                 case 'V':
     431           0 :                         return print_version();
     432           0 :                 case 'h':
     433           0 :                         return help();
     434           0 :                 case '?':
     435           0 :                         return -EINVAL;
     436           0 :                 default:
     437           0 :                         assert_not_reached("Unknown option");
     438             :                 }
     439             : 
     440           0 :         if (action == ACTION_DEVICE_ID_FILE) {
     441           0 :                 if (argv[optind])
     442           0 :                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     443             :                                                "Positional arguments are not allowed with -d/--device-id-of-file.");
     444           0 :                 assert(name);
     445           0 :                 return stat_device(name, arg_export, arg_export_prefix);
     446             :         }
     447             : 
     448           0 :         r = strv_extend_strv(&devices, argv + optind, false);
     449           0 :         if (r < 0)
     450           0 :                 return log_error_errno(r, "Failed to build argument list: %m");
     451             : 
     452           0 :         if (strv_isempty(devices))
     453           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     454             :                                        "A device name or path is required");
     455           0 :         if (action == ACTION_ATTRIBUTE_WALK && strv_length(devices) > 1)
     456           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     457             :                                        "Only one device may be specified with -a/--attribute-walk");
     458             : 
     459             :         char **p;
     460           0 :         STRV_FOREACH(p, devices) {
     461           0 :                 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
     462             : 
     463           0 :                 r = find_device(*p, NULL, &device);
     464           0 :                 if (r == -EINVAL)
     465           0 :                         return log_error_errno(r, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys or a unit name: %m", *p);
     466           0 :                 if (r < 0)
     467           0 :                         return log_error_errno(r, "Unknown device \"%s\": %m",  *p);
     468             : 
     469           0 :                 if (arg_wait_for_initialization_timeout > 0) {
     470             :                         sd_device *d;
     471             : 
     472           0 :                         r = device_wait_for_initialization(device, NULL, arg_wait_for_initialization_timeout, &d);
     473           0 :                         if (r < 0)
     474           0 :                                 return r;
     475             : 
     476           0 :                         sd_device_unref(device);
     477           0 :                         device = d;
     478             :                 }
     479             : 
     480           0 :                 if (action == ACTION_QUERY)
     481           0 :                         r = query_device(query, device);
     482           0 :                 else if (action == ACTION_ATTRIBUTE_WALK)
     483           0 :                         r = print_device_chain(device);
     484             :                 else
     485           0 :                         assert_not_reached("Unknown action");
     486           0 :                 if (r < 0)
     487           0 :                         return r;
     488             :         }
     489             : 
     490           0 :         return 0;
     491             : }

Generated by: LCOV version 1.14