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