chiark / gitweb /
udevadm,..: make --help output of udev tools more like the output of the various...
[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 void help(void) {
262
263         printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
264                "Query sysfs or the udev database.\n\n"
265                "  -h --help                   Print this message\n"
266                "     --version                Print version of the program\n"
267                "  -q --query=TYPE             Query device information:\n"
268                "       name                     Name of device node\n"
269                "       symlink                  Pointing to node\n"
270                "       path                     sysfs device path\n"
271                "       property                 The device properties\n"
272                "       all                      All values\n"
273                "  -p --path=SYSPATH           sysfs device path used for query or attribute walk\n"
274                "  -n --name=NAME              Node or symlink name used for query or attribute walk\n"
275                "  -r --root                   Prepend dev directory to path names\n"
276                "  -a --attribute-walk         Print all key matches walking along the chain\n"
277                "                              of parent devices\n"
278                "  -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
279                "  -x --export                 Export key/value pairs\n"
280                "  -P --export-prefix          Export the key name with a prefix\n"
281                "  -e --export-db              Export the content of the udev database\n"
282                "  -c --cleanup-db             Clean up the udev database\n"
283                , program_invocation_short_name);
284 }
285
286 static int uinfo(struct udev *udev, int argc, char *argv[]) {
287         _cleanup_udev_device_unref_ struct udev_device *device = NULL;
288         bool root = 0;
289         bool export = 0;
290         const char *export_prefix = NULL;
291         char name[UTIL_PATH_SIZE];
292         struct udev_list_entry *list_entry;
293         int c;
294
295         static const struct option options[] = {
296                 { "name",              required_argument, NULL, 'n' },
297                 { "path",              required_argument, NULL, 'p' },
298                 { "query",             required_argument, NULL, 'q' },
299                 { "attribute-walk",    no_argument,       NULL, 'a' },
300                 { "cleanup-db",        no_argument,       NULL, 'c' },
301                 { "export-db",         no_argument,       NULL, 'e' },
302                 { "root",              no_argument,       NULL, 'r' },
303                 { "device-id-of-file", required_argument, NULL, 'd' },
304                 { "export",            no_argument,       NULL, 'x' },
305                 { "export-prefix",     required_argument, NULL, 'P' },
306                 { "version",           no_argument,       NULL, 'V' },
307                 { "help",              no_argument,       NULL, 'h' },
308                 {}
309         };
310
311         enum action_type {
312                 ACTION_QUERY,
313                 ACTION_ATTRIBUTE_WALK,
314                 ACTION_DEVICE_ID_FILE,
315         } action = ACTION_QUERY;
316
317         enum query_type {
318                 QUERY_NAME,
319                 QUERY_PATH,
320                 QUERY_SYMLINK,
321                 QUERY_PROPERTY,
322                 QUERY_ALL,
323         } query = QUERY_ALL;
324
325         while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
326                 switch (c) {
327                 case 'n': {
328                         if (device != NULL) {
329                                 fprintf(stderr, "device already specified\n");
330                                 return 2;
331                         }
332
333                         device = find_device(udev, optarg, "/dev/");
334                         if (device == NULL) {
335                                 fprintf(stderr, "device node not found\n");
336                                 return 2;
337                         }
338                         break;
339                 }
340                 case 'p':
341                         if (device != NULL) {
342                                 fprintf(stderr, "device already specified\n");
343                                 return 2;
344                         }
345
346                         device = find_device(udev, optarg, "/sys");
347                         if (device == NULL) {
348                                 fprintf(stderr, "syspath not found\n");
349                                 return 2;
350                         }
351                         break;
352                 case 'q':
353                         action = ACTION_QUERY;
354                         if (streq(optarg, "property") || streq(optarg, "env"))
355                                 query = QUERY_PROPERTY;
356                         else if (streq(optarg, "name"))
357                                 query = QUERY_NAME;
358                         else if (streq(optarg, "symlink"))
359                                 query = QUERY_SYMLINK;
360                         else if (streq(optarg, "path"))
361                                 query = QUERY_PATH;
362                         else if (streq(optarg, "all"))
363                                 query = QUERY_ALL;
364                         else {
365                                 fprintf(stderr, "unknown query type\n");
366                                 return 3;
367                         }
368                         break;
369                 case 'r':
370                         root = true;
371                         break;
372                 case 'd':
373                         action = ACTION_DEVICE_ID_FILE;
374                         strscpy(name, sizeof(name), optarg);
375                         break;
376                 case 'a':
377                         action = ACTION_ATTRIBUTE_WALK;
378                         break;
379                 case 'e':
380                         export_devices(udev);
381                         return 0;
382                 case 'c':
383                         cleanup_db(udev);
384                         return 0;
385                 case 'x':
386                         export = true;
387                         break;
388                 case 'P':
389                         export_prefix = optarg;
390                         break;
391                 case 'V':
392                         printf("%s\n", VERSION);
393                         return 0;
394                 case 'h':
395                         help();
396                         return 0;
397                 default:
398                         return 1;
399                 }
400
401         switch (action) {
402         case ACTION_QUERY:
403                 if (!device) {
404                         if (!argv[optind]) {
405                                 help();
406                                 return 2;
407                         }
408                         device = find_device(udev, argv[optind], NULL);
409                         if (!device) {
410                                 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
411                                 return 4;
412                         }
413                 }
414
415                 switch(query) {
416                 case QUERY_NAME: {
417                         const char *node = udev_device_get_devnode(device);
418
419                         if (node == NULL) {
420                                 fprintf(stderr, "no device node found\n");
421                                 return 5;
422                         }
423
424                         if (root)
425                                 printf("%s\n", udev_device_get_devnode(device));
426                         else
427                                 printf("%s\n", udev_device_get_devnode(device) + strlen("/dev/"));
428                         break;
429                 }
430                 case QUERY_SYMLINK:
431                         list_entry = udev_device_get_devlinks_list_entry(device);
432                         while (list_entry != NULL) {
433                                 if (root)
434                                         printf("%s", udev_list_entry_get_name(list_entry));
435                                 else
436                                         printf("%s", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
437                                 list_entry = udev_list_entry_get_next(list_entry);
438                                 if (list_entry != NULL)
439                                         printf(" ");
440                         }
441                         printf("\n");
442                         break;
443                 case QUERY_PATH:
444                         printf("%s\n", udev_device_get_devpath(device));
445                         return 0;
446                 case QUERY_PROPERTY:
447                         list_entry = udev_device_get_properties_list_entry(device);
448                         while (list_entry != NULL) {
449                                 if (export) {
450                                         const char *prefix = export_prefix;
451
452                                         if (prefix == NULL)
453                                                 prefix = "";
454                                         printf("%s%s='%s'\n", prefix,
455                                                udev_list_entry_get_name(list_entry),
456                                                udev_list_entry_get_value(list_entry));
457                                 } else {
458                                         printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
459                                 }
460                                 list_entry = udev_list_entry_get_next(list_entry);
461                         }
462                         break;
463                 case QUERY_ALL:
464                         print_record(device);
465                         break;
466                 default:
467                         assert_not_reached("unknown query type");
468                 }
469                 break;
470         case ACTION_ATTRIBUTE_WALK:
471                 if (!device && argv[optind]) {
472                         device = find_device(udev, argv[optind], NULL);
473                         if (!device) {
474                                 fprintf(stderr, "Unknown device, absolute path in /dev/ or /sys expected.\n");
475                                 return 4;
476                         }
477                 }
478                 if (!device) {
479                         fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
480                         return 4;
481                 }
482                 print_device_chain(device);
483                 break;
484         case ACTION_DEVICE_ID_FILE:
485                 if (stat_device(name, export, export_prefix) != 0)
486                         return 1;
487                 break;
488         }
489
490         return 0;
491 }
492
493 const struct udevadm_cmd udevadm_info = {
494         .name = "info",
495         .cmd = uinfo,
496         .help = "Query sysfs or the udev database",
497 };