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