• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2004-2009 Kay Sievers <kay@vrfy.org>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <stdlib.h>
19 #include <string.h>
20 #include <stdio.h>
21 #include <stddef.h>
22 #include <ctype.h>
23 #include <stdarg.h>
24 #include <unistd.h>
25 #include <dirent.h>
26 #include <errno.h>
27 #include <getopt.h>
28 #include <fcntl.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <sys/sysmacros.h>
32 
33 #include "udev.h"
34 #include "udev-util.h"
35 #include "udevadm-util.h"
36 
skip_attribute(const char * name)37 static bool skip_attribute(const char *name) {
38         static const char* const skip[] = {
39                 "uevent",
40                 "dev",
41                 "modalias",
42                 "resource",
43                 "driver",
44                 "subsystem",
45                 "module",
46         };
47         unsigned int i;
48 
49         for (i = 0; i < ELEMENTSOF(skip); i++)
50                 if (streq(name, skip[i]))
51                         return true;
52         return false;
53 }
54 
print_all_attributes(struct udev_device * device,const char * key)55 static void print_all_attributes(struct udev_device *device, const char *key) {
56         struct udev_list_entry *sysattr;
57 
58         udev_list_entry_foreach(sysattr, udev_device_get_sysattr_list_entry(device)) {
59                 const char *name;
60                 const char *value;
61                 size_t len;
62 
63                 name = udev_list_entry_get_name(sysattr);
64                 if (skip_attribute(name))
65                         continue;
66 
67                 value = udev_device_get_sysattr_value(device, name);
68                 if (value == NULL)
69                         continue;
70 
71                 /* skip any values that look like a path */
72                 if (value[0] == '/')
73                         continue;
74 
75                 /* skip nonprintable attributes */
76                 len = strlen(value);
77                 while (len > 0 && isprint(value[len-1]))
78                         len--;
79                 if (len > 0)
80                         continue;
81 
82                 printf("    %s{%s}==\"%s\"\n", key, name, value);
83         }
84         printf("\n");
85 }
86 
print_device_chain(struct udev_device * device)87 static int print_device_chain(struct udev_device *device) {
88         struct udev_device *device_parent;
89         const char *str;
90 
91         printf("\n"
92                "Udevadm info starts with the device specified by the devpath and then\n"
93                "walks up the chain of parent devices. It prints for every device\n"
94                "found, all possible attributes in the udev rules key format.\n"
95                "A rule to match, can be composed by the attributes of the device\n"
96                "and the attributes from one single parent device.\n"
97                "\n");
98 
99         printf("  looking at device '%s':\n", udev_device_get_devpath(device));
100         printf("    KERNEL==\"%s\"\n", udev_device_get_sysname(device));
101         str = udev_device_get_subsystem(device);
102         if (str == NULL)
103                 str = "";
104         printf("    SUBSYSTEM==\"%s\"\n", str);
105         str = udev_device_get_driver(device);
106         if (str == NULL)
107                 str = "";
108         printf("    DRIVER==\"%s\"\n", str);
109         print_all_attributes(device, "ATTR");
110 
111         device_parent = device;
112         do {
113                 device_parent = udev_device_get_parent(device_parent);
114                 if (device_parent == NULL)
115                         break;
116                 printf("  looking at parent device '%s':\n", udev_device_get_devpath(device_parent));
117                 printf("    KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent));
118                 str = udev_device_get_subsystem(device_parent);
119                 if (str == NULL)
120                         str = "";
121                 printf("    SUBSYSTEMS==\"%s\"\n", str);
122                 str = udev_device_get_driver(device_parent);
123                 if (str == NULL)
124                         str = "";
125                 printf("    DRIVERS==\"%s\"\n", str);
126                 print_all_attributes(device_parent, "ATTRS");
127         } while (device_parent != NULL);
128 
129         return 0;
130 }
131 
print_record(struct udev_device * device)132 static void print_record(struct udev_device *device) {
133         const char *str;
134         int i;
135         struct udev_list_entry *list_entry;
136 
137         printf("P: %s\n", udev_device_get_devpath(device));
138 
139         str = udev_device_get_devnode(device);
140         if (str != NULL)
141                 printf("N: %s\n", str + strlen("/dev/"));
142 
143         i = udev_device_get_devlink_priority(device);
144         if (i != 0)
145                 printf("L: %i\n", i);
146 
147         udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device))
148                 printf("S: %s\n", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
149 
150         udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))
151                 printf("E: %s=%s\n",
152                        udev_list_entry_get_name(list_entry),
153                        udev_list_entry_get_value(list_entry));
154         printf("\n");
155 }
156 
stat_device(const char * name,bool export,const char * prefix)157 static int stat_device(const char *name, bool export, const char *prefix) {
158         struct stat statbuf;
159 
160         if (stat(name, &statbuf) != 0)
161                 return -1;
162 
163         if (export) {
164                 if (prefix == NULL)
165                         prefix = "INFO_";
166                 printf("%sMAJOR=%u\n"
167                        "%sMINOR=%u\n",
168                        prefix, major(statbuf.st_dev),
169                        prefix, minor(statbuf.st_dev));
170         } else
171                 printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev));
172         return 0;
173 }
174 
export_devices(struct udev * udev)175 static int export_devices(struct udev *udev) {
176         struct udev_enumerate *udev_enumerate;
177         struct udev_list_entry *list_entry;
178 
179         udev_enumerate = udev_enumerate_new(udev);
180         if (udev_enumerate == NULL)
181                 return -1;
182         udev_enumerate_scan_devices(udev_enumerate);
183         udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
184                 struct udev_device *device;
185 
186                 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
187                 if (device != NULL) {
188                         print_record(device);
189                         udev_device_unref(device);
190                 }
191         }
192         udev_enumerate_unref(udev_enumerate);
193         return 0;
194 }
195 
cleanup_dir(DIR * dir,mode_t mask,int depth)196 static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
197         struct dirent *dent;
198 
199         if (depth <= 0)
200                 return;
201 
202         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
203                 struct stat stats;
204 
205                 if (dent->d_name[0] == '.')
206                         continue;
207                 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
208                         continue;
209                 if ((stats.st_mode & mask) != 0)
210                         continue;
211                 if (S_ISDIR(stats.st_mode)) {
212                         _cleanup_closedir_ DIR *dir2;
213 
214                         dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
215                         if (dir2 != NULL)
216                                 cleanup_dir(dir2, mask, depth-1);
217 
218                         (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
219                 } else
220                         (void) unlinkat(dirfd(dir), dent->d_name, 0);
221         }
222 }
223 
cleanup_db(struct udev * udev)224 static void cleanup_db(struct udev *udev) {
225         DIR *dir;
226 
227         unlink(UDEV_ROOT_RUN "/udev/queue.bin");
228 
229         dir = opendir(UDEV_ROOT_RUN "/udev/data");
230         if (dir != NULL) {
231                 cleanup_dir(dir, S_ISVTX, 1);
232                 closedir(dir);
233         }
234 
235         dir = opendir(UDEV_ROOT_RUN "/udev/links");
236         if (dir != NULL) {
237                 cleanup_dir(dir, 0, 2);
238                 closedir(dir);
239         }
240 
241         dir = opendir(UDEV_ROOT_RUN "/udev/tags");
242         if (dir != NULL) {
243                 cleanup_dir(dir, 0, 2);
244                 closedir(dir);
245         }
246 
247         dir = opendir(UDEV_ROOT_RUN "/udev/static_node-tags");
248         if (dir != NULL) {
249                 cleanup_dir(dir, 0, 2);
250                 closedir(dir);
251         }
252 
253         dir = opendir(UDEV_ROOT_RUN "/udev/watch");
254         if (dir != NULL) {
255                 cleanup_dir(dir, 0, 1);
256                 closedir(dir);
257         }
258 }
259 
help(void)260 static void help(void) {
261 
262         printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
263                "Query sysfs or the udev database.\n\n"
264                "  -h --help                   Print this message\n"
265                "     --version                Print version of the program\n"
266                "  -q --query=TYPE             Query device information:\n"
267                "       name                     Name of device node\n"
268                "       symlink                  Pointing to node\n"
269                "       path                     sysfs device path\n"
270                "       property                 The device properties\n"
271                "       all                      All values\n"
272                "  -p --path=SYSPATH           sysfs device path used for query or attribute walk\n"
273                "  -n --name=NAME              Node or symlink name used for query or attribute walk\n"
274                "  -r --root                   Prepend dev directory to path names\n"
275                "  -a --attribute-walk         Print all key matches walking along the chain\n"
276                "                              of parent devices\n"
277                "  -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
278                "  -x --export                 Export key/value pairs\n"
279                "  -P --export-prefix          Export the key name with a prefix\n"
280                "  -e --export-db              Export the content of the udev database\n"
281                "  -c --cleanup-db             Clean up the udev database\n"
282                , program_invocation_short_name);
283 }
284 
uinfo(struct udev * udev,int argc,char * argv[])285 static int uinfo(struct udev *udev, int argc, char *argv[]) {
286         _cleanup_udev_device_unref_ struct udev_device *device = NULL;
287         bool root = 0;
288         bool export = 0;
289         const char *export_prefix = NULL;
290         char name[UTIL_PATH_SIZE];
291         struct udev_list_entry *list_entry;
292         int c;
293 
294         static const struct option options[] = {
295                 { "name",              required_argument, NULL, 'n' },
296                 { "path",              required_argument, NULL, 'p' },
297                 { "query",             required_argument, NULL, 'q' },
298                 { "attribute-walk",    no_argument,       NULL, 'a' },
299                 { "cleanup-db",        no_argument,       NULL, 'c' },
300                 { "export-db",         no_argument,       NULL, 'e' },
301                 { "root",              no_argument,       NULL, 'r' },
302                 { "device-id-of-file", required_argument, NULL, 'd' },
303                 { "export",            no_argument,       NULL, 'x' },
304                 { "export-prefix",     required_argument, NULL, 'P' },
305                 { "version",           no_argument,       NULL, 'V' },
306                 { "help",              no_argument,       NULL, 'h' },
307                 {}
308         };
309 
310         enum action_type {
311                 ACTION_QUERY,
312                 ACTION_ATTRIBUTE_WALK,
313                 ACTION_DEVICE_ID_FILE,
314         } action = ACTION_QUERY;
315 
316         enum query_type {
317                 QUERY_NAME,
318                 QUERY_PATH,
319                 QUERY_SYMLINK,
320                 QUERY_PROPERTY,
321                 QUERY_ALL,
322         } query = QUERY_ALL;
323 
324         while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
325                 switch (c) {
326                 case 'n': {
327                         if (device != NULL) {
328                                 fprintf(stderr, "device already specified\n");
329                                 return 2;
330                         }
331 
332                         device = find_device(udev, optarg, "/dev/");
333                         if (device == NULL) {
334                                 fprintf(stderr, "device node not found\n");
335                                 return 2;
336                         }
337                         break;
338                 }
339                 case 'p':
340                         if (device != NULL) {
341                                 fprintf(stderr, "device already specified\n");
342                                 return 2;
343                         }
344 
345                         device = find_device(udev, optarg, "/sys");
346                         if (device == NULL) {
347                                 fprintf(stderr, "syspath not found\n");
348                                 return 2;
349                         }
350                         break;
351                 case 'q':
352                         action = ACTION_QUERY;
353                         if (streq(optarg, "property") || streq(optarg, "env"))
354                                 query = QUERY_PROPERTY;
355                         else if (streq(optarg, "name"))
356                                 query = QUERY_NAME;
357                         else if (streq(optarg, "symlink"))
358                                 query = QUERY_SYMLINK;
359                         else if (streq(optarg, "path"))
360                                 query = QUERY_PATH;
361                         else if (streq(optarg, "all"))
362                                 query = QUERY_ALL;
363                         else {
364                                 fprintf(stderr, "unknown query type\n");
365                                 return 3;
366                         }
367                         break;
368                 case 'r':
369                         root = true;
370                         break;
371                 case 'd':
372                         action = ACTION_DEVICE_ID_FILE;
373                         strscpy(name, sizeof(name), optarg);
374                         break;
375                 case 'a':
376                         action = ACTION_ATTRIBUTE_WALK;
377                         break;
378                 case 'e':
379                         export_devices(udev);
380                         return 0;
381                 case 'c':
382                         cleanup_db(udev);
383                         return 0;
384                 case 'x':
385                         export = true;
386                         break;
387                 case 'P':
388                         export_prefix = optarg;
389                         break;
390                 case 'V':
391                         printf("%s\n", UDEV_VERSION);
392                         return 0;
393                 case 'h':
394                         help();
395                         return 0;
396                 default:
397                         return 1;
398                 }
399 
400         switch (action) {
401         case ACTION_QUERY:
402                 if (!device) {
403                         if (!argv[optind]) {
404                                 help();
405                                 return 2;
406                         }
407                         device = find_device(udev, argv[optind], NULL);
408                         if (!device) {
409                                 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
410                                 return 4;
411                         }
412                 }
413 
414                 switch(query) {
415                 case QUERY_NAME: {
416                         const char *node = udev_device_get_devnode(device);
417 
418                         if (node == NULL) {
419                                 fprintf(stderr, "no device node found\n");
420                                 return 5;
421                         }
422 
423                         if (root)
424                                 printf("%s\n", udev_device_get_devnode(device));
425                         else
426                                 printf("%s\n", udev_device_get_devnode(device) + strlen("/dev/"));
427                         break;
428                 }
429                 case QUERY_SYMLINK:
430                         list_entry = udev_device_get_devlinks_list_entry(device);
431                         while (list_entry != NULL) {
432                                 if (root)
433                                         printf("%s", udev_list_entry_get_name(list_entry));
434                                 else
435                                         printf("%s", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
436                                 list_entry = udev_list_entry_get_next(list_entry);
437                                 if (list_entry != NULL)
438                                         printf(" ");
439                         }
440                         printf("\n");
441                         break;
442                 case QUERY_PATH:
443                         printf("%s\n", udev_device_get_devpath(device));
444                         return 0;
445                 case QUERY_PROPERTY:
446                         list_entry = udev_device_get_properties_list_entry(device);
447                         while (list_entry != NULL) {
448                                 if (export) {
449                                         const char *prefix = export_prefix;
450 
451                                         if (prefix == NULL)
452                                                 prefix = "";
453                                         printf("%s%s='%s'\n", prefix,
454                                                udev_list_entry_get_name(list_entry),
455                                                udev_list_entry_get_value(list_entry));
456                                 } else {
457                                         printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
458                                 }
459                                 list_entry = udev_list_entry_get_next(list_entry);
460                         }
461                         break;
462                 case QUERY_ALL:
463                         print_record(device);
464                         break;
465                 default:
466                         assert_not_reached("unknown query type");
467                 }
468                 break;
469         case ACTION_ATTRIBUTE_WALK:
470                 if (!device && argv[optind]) {
471                         device = find_device(udev, argv[optind], NULL);
472                         if (!device) {
473                                 fprintf(stderr, "Unknown device, absolute path in /dev/ or /sys expected.\n");
474                                 return 4;
475                         }
476                 }
477                 if (!device) {
478                         fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
479                         return 4;
480                 }
481                 print_device_chain(device);
482                 break;
483         case ACTION_DEVICE_ID_FILE:
484                 if (stat_device(name, export, export_prefix) != 0)
485                         return 1;
486                 break;
487         }
488 
489         return 0;
490 }
491 
492 const struct udevadm_cmd udevadm_info = {
493         .name = "info",
494         .cmd = uinfo,
495         .help = "Query sysfs or the udev database",
496 };
497